CacheManager.java revision cedb3a7e5849fd16e939add1ac6f5586467b8c68
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;
207cfa90fee54f44831ac492891d1c123601c2a262Jesse Wilsonimport android.net.http.AndroidHttpClient;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.http.Headers;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.FileUtils;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileInputStream;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileNotFoundException;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileOutputStream;
282036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Klobaimport java.io.FilenameFilter;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.InputStream;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.OutputStream;
322036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Klobaimport java.util.List;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.Map;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3545a9a14006214e6478311ffcb980e766702d5a76Doug Zongker
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.bouncycastle.crypto.Digest;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport org.bouncycastle.crypto.digests.SHA1Digest;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The class CacheManager provides the persistent cache of content that is
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * received over the network. The component handles parsing of HTTP headers and
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * utilizes the relevant cache headers to determine if the content should be
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * stored and if so, how long it is valid for. Network requests are provided to
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * this component and if they can not be resolved by the cache, the HTTP headers
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * are attached, as appropriate, to the request for revalidation of content. The
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * class also manages the cache size.
47c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick *
48c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick * @deprecated Access to the HTTP cache will be removed in a future release.
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
50c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick@Deprecated
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic final class CacheManager {
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String LOGTAG = "cache";
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final String HEADER_KEY_IFMODIFIEDSINCE = "if-modified-since";
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final String HEADER_KEY_IFNONEMATCH = "if-none-match";
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String NO_STORE = "no-store";
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String NO_CACHE = "no-cache";
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String MAX_AGE = "max-age";
61a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu    private static final String MANIFEST_MIME = "text/cache-manifest";
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static long CACHE_THRESHOLD = 6 * 1024 * 1024;
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static long CACHE_TRIM_AMOUNT = 2 * 1024 * 1024;
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
66998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba    // Limit the maximum cache file size to half of the normal capacity
67998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba    static long CACHE_MAX_SIZE = (CACHE_THRESHOLD - CACHE_TRIM_AMOUNT) / 2;
68998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean mDisabled;
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Reference count the enable/disable transaction
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static int mRefCount;
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // trimCacheIfNeeded() is called when a page is fully loaded. But JavaScript
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // can load the content, e.g. in a slideshow, continuously, so we need to
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // trim the cache on a timer base too. endCacheTransaction() is called on a
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // timer base. We share the same timer with less frequent update.
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static int mTrimCacheCount = 0;
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TRIM_CACHE_INTERVAL = 5;
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static WebViewDatabase mDataBase;
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static File mBaseDir;
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Flag to clear the cache when the CacheManager is initialized
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean mClearCacheOnInit = false;
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
87f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch    /**
88f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch     * This class represents a resource retrieved from the HTTP cache.
89f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch     * Instances of this class can be obtained by invoking the
90f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch     * CacheManager.getCacheFile() method.
91c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     *
92c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     * @deprecated Access to the HTTP cache will be removed in a future release.
93f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch     */
94c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick    @Deprecated
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class CacheResult {
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // these fields are saved to the database
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int httpStatusCode;
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long contentLength;
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long expires;
100e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba        String expiresString;
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String localPath;
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String lastModified;
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String etag;
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String mimeType;
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String location;
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String encoding;
1070b956e1353a691674cb22c899c5a444b92532b60Grace Kloba        String contentdisposition;
10860708a75120c4469dc2683485301ff9ee3b022e0Leon Clarke        String crossDomain;
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // these fields are NOT saved to the database
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        InputStream inStream;
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        OutputStream outStream;
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File outFile;
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getHttpStatusCode() {
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return httpStatusCode;
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public long getContentLength() {
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return contentLength;
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getLocalPath() {
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return localPath;
1259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public long getExpires() {
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return expires;
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
131e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba        public String getExpiresString() {
132e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba            return expiresString;
133e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba        }
134e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getLastModified() {
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return lastModified;
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getETag() {
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return etag;
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getMimeType() {
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mimeType;
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getLocation() {
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return location;
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncoding() {
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return encoding;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1550b956e1353a691674cb22c899c5a444b92532b60Grace Kloba        public String getContentDisposition() {
1560b956e1353a691674cb22c899c5a444b92532b60Grace Kloba            return contentdisposition;
1570b956e1353a691674cb22c899c5a444b92532b60Grace Kloba        }
1580b956e1353a691674cb22c899c5a444b92532b60Grace Kloba
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // For out-of-package access to the underlying streams.
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public InputStream getInputStream() {
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return inStream;
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public OutputStream getOutputStream() {
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return outStream;
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // These fields can be set manually.
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setInputStream(InputStream stream) {
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.inStream = stream;
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setEncoding(String encoding) {
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.encoding = encoding;
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
176b67529b905440e2ba550742773b927abad882c19Iain Merrick
177b67529b905440e2ba550742773b927abad882c19Iain Merrick        /**
178b67529b905440e2ba550742773b927abad882c19Iain Merrick         * @hide
179b67529b905440e2ba550742773b927abad882c19Iain Merrick         */
180b67529b905440e2ba550742773b927abad882c19Iain Merrick        public void setContentLength(long contentLength) {
181b67529b905440e2ba550742773b927abad882c19Iain Merrick            this.contentLength = contentLength;
182b67529b905440e2ba550742773b927abad882c19Iain Merrick        }
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
18667ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * Initialize the CacheManager.
18767ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     *
18867ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * Note that this is called automatically when a {@link android.webkit.WebView} is created.
18967ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     *
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context The application context.
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void init(Context context) {
193808751fe7ac16bf7224cba284a318695d8093355Steve Block        if (JniUtil.useChromiumHttpStack()) {
194e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // This isn't actually where the real cache lives, but where we put files for the
195e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // purpose of getCacheFile().
196e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            mBaseDir = new File(context.getCacheDir(), "webviewCacheChromiumStaging");
197e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            if (!mBaseDir.exists()) {
198e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block                mBaseDir.mkdirs();
199e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            } else {
200e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block                // TODO: Should we clear out old files?
201e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            }
202808751fe7ac16bf7224cba284a318695d8093355Steve Block            return;
203808751fe7ac16bf7224cba284a318695d8093355Steve Block        }
204808751fe7ac16bf7224cba284a318695d8093355Steve Block
20501d0fbfa683012623f030ec75a63e1a9fabcb916Romain Guy        mDataBase = WebViewDatabase.getInstance(context.getApplicationContext());
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBaseDir = new File(context.getCacheDir(), "webviewCache");
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (createCacheDirectory() && mClearCacheOnInit) {
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            removeAllCacheFiles();
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mClearCacheOnInit = false;
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
21267ba204aa23e7d5a96ad241a1623e44976b51741Steve Block
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Create the cache directory if it does not already exist.
21567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     *
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if the cache directory didn't exist and was created.
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static private boolean createCacheDirectory() {
219808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
220808751fe7ac16bf7224cba284a318695d8093355Steve Block
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!mBaseDir.exists()) {
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if(!mBaseDir.mkdirs()) {
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.w(LOGTAG, "Unable to create webviewCache directory");
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            FileUtils.setPermissions(
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mBaseDir.toString(),
22811c1a0be5d8332e662ccc00f4cb66d821e79c8dfJohn Reck                    FileUtils.S_IRWXU | FileUtils.S_IRWXG,
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    -1, -1);
23067ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            // If we did create the directory, we need to flush
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the cache database. The directory could be recreated
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // because the system flushed all the data/cache directories
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // to free up disk space.
2342036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            // delete rows in the cache database
2352036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            WebViewWorker.getHandler().sendEmptyMessage(
2362036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                    WebViewWorker.MSG_CLEAR_CACHE);
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
24367ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * Get the base directory of the cache. Together with the local path of the CacheResult,
24467ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * obtained from {@link android.webkit.CacheManager.CacheResult#getLocalPath}, this
24567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * identifies the cache file.
246c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     *
247cedb3a7e5849fd16e939add1ac6f5586467b8c68Kristian Monsen     * Cache files are not guaranteed to be in this directory before
248cedb3a7e5849fd16e939add1ac6f5586467b8c68Kristian Monsen     * CacheManager#getCacheFile(String, Map<String, String>) is called.
249cedb3a7e5849fd16e939add1ac6f5586467b8c68Kristian Monsen     *
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return File The base directory of the cache.
251c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     *
252c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     * @deprecated Access to the HTTP cache will be removed in a future release.
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
254c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick    @Deprecated
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static File getCacheFileBaseDir() {
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mBaseDir;
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
26067ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * Sets whether the cache is disabled.
26167ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     *
26267ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * @param disabled Whether the cache should be disabled
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void setCacheDisabled(boolean disabled) {
265808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
266808751fe7ac16bf7224cba284a318695d8093355Steve Block
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (disabled == mDisabled) {
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDisabled = disabled;
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDisabled) {
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            removeAllCacheFiles();
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
27767ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * Whether the cache is disabled.
278c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     *
27967ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * @return return Whether the cache is disabled
280c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     *
281c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     * @deprecated Access to the HTTP cache will be removed in a future release.
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
283c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick    @Deprecated
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean cacheDisabled() {
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDisabled;
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2882036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // only called from WebViewWorkerThread
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // make sure to call enableTransaction/disableTransaction in pair
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static boolean enableTransaction() {
291808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
292808751fe7ac16bf7224cba284a318695d8093355Steve Block
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (++mRefCount == 1) {
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDataBase.startCacheTransaction();
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3002036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // only called from WebViewWorkerThread
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // make sure to call enableTransaction/disableTransaction in pair
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static boolean disableTransaction() {
303808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
304808751fe7ac16bf7224cba284a318695d8093355Steve Block
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (--mRefCount == 0) {
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDataBase.endCacheTransaction();
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3122036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // only called from WebViewWorkerThread
3132036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // make sure to call startTransaction/endTransaction in pair
3142036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    static boolean startTransaction() {
315808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
316808751fe7ac16bf7224cba284a318695d8093355Steve Block
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDataBase.startCacheTransaction();
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3202036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // only called from WebViewWorkerThread
3212036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // make sure to call startTransaction/endTransaction in pair
3222036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    static boolean endTransaction() {
323808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
324808751fe7ac16bf7224cba284a318695d8093355Steve Block
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean ret = mDataBase.endCacheTransaction();
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (++mTrimCacheCount >= TRIM_CACHE_INTERVAL) {
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTrimCacheCount = 0;
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            trimCacheIfNeeded();
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ret;
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3332036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // only called from WebCore Thread
3342036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // make sure to call startCacheTransaction/endCacheTransaction in pair
3352036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    /**
336201480c50e6f0ca7a0df7dfb14ba748c710969eaJoe Onorato     * @deprecated Always returns false.
3372036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba     */
3382036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    @Deprecated
3392036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    public static boolean startCacheTransaction() {
3402036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        return false;
3412036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    }
3422036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba
3432036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // only called from WebCore Thread
3442036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // make sure to call startCacheTransaction/endCacheTransaction in pair
3452036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    /**
346201480c50e6f0ca7a0df7dfb14ba748c710969eaJoe Onorato     * @deprecated Always returns false.
3472036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba     */
3482036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    @Deprecated
3492036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    public static boolean endCacheTransaction() {
3502036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        return false;
3512036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    }
3522036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
35467ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * Given a URL, returns the corresponding CacheResult if it exists, or null otherwise.
35567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     *
356e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block     * The input stream of the CacheEntry object is initialized and opened and should be closed by
357e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block     * the caller when access to the underlying file is no longer required.
35867ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * If a non-zero value is provided for the headers map, and the cache entry needs validation,
35967ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE will be set in headers.
36067ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     *
36167ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * @return The CacheResult for the given URL
362c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     *
363c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     * @deprecated Access to the HTTP cache will be removed in a future release.
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
365c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick    @Deprecated
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CacheResult getCacheFile(String url,
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Map<String, String> headers) {
3688c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        return getCacheFile(url, 0, headers);
3698c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    }
3708c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
3718c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    static CacheResult getCacheFile(String url, long postIdentifier,
3728c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            Map<String, String> headers) {
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDisabled) {
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
377808751fe7ac16bf7224cba284a318695d8093355Steve Block        if (JniUtil.useChromiumHttpStack()) {
378e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            CacheResult result = nativeGetCacheResult(url);
379e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            if (result == null) {
380e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block                return null;
381e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            }
382e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // A temporary local file will have been created native side and localPath set
383e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // appropriately.
384e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            File src = new File(mBaseDir, result.localPath);
385e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            try {
386e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block                // Open the file here so that even if it is deleted, the content
387e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block                // is still readable by the caller until close() is called.
388e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block                result.inStream = new FileInputStream(src);
389e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            } catch (FileNotFoundException e) {
390e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block                Log.v(LOGTAG, "getCacheFile(): Failed to open file: " + e);
391e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block                // TODO: The files in the cache directory can be removed by the
392e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block                // system. If it is gone, what should we do?
393e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block                return null;
394e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            }
395e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            return result;
396808751fe7ac16bf7224cba284a318695d8093355Steve Block        }
3978c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
398808751fe7ac16bf7224cba284a318695d8093355Steve Block        String databaseKey = getDatabaseKey(url, postIdentifier);
3998c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        CacheResult result = mDataBase.getCache(databaseKey);
40067ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        if (result == null) {
40167ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            return null;
40267ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        }
40367ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        if (result.contentLength == 0) {
40467ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            if (!isCachableRedirect(result.httpStatusCode)) {
40567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                // This should not happen. If it does, remove it.
40667ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                mDataBase.removeCache(databaseKey);
40767ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                return null;
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
41067ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            File src = new File(mBaseDir, result.localPath);
41167ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            try {
41267ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                // Open the file here so that even if it is deleted, the content
41367ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                // is still readable by the caller until close() is called.
41467ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                result.inStream = new FileInputStream(src);
41567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            } catch (FileNotFoundException e) {
41667ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                // The files in the cache directory can be removed by the
41767ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                // system. If it is gone, clean up the database.
41867ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                mDataBase.removeCache(databaseKey);
41967ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                return null;
42067ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            }
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
42367ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        // A null value for headers is used by CACHE_MODE_CACHE_ONLY to imply
42467ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        // that we should provide the cache result even if it is expired.
42567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        // Note that a negative expires value means a time in the far future.
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (headers != null && result.expires >= 0
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && result.expires <= System.currentTimeMillis()) {
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (result.lastModified == null && result.etag == null) {
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
43167ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            // Return HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE
43267ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            // for requesting validation.
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (result.etag != null) {
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                headers.put(HEADER_KEY_IFNONEMATCH, result.etag);
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (result.lastModified != null) {
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                headers.put(HEADER_KEY_IFMODIFIEDSINCE, result.lastModified);
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4412e5c150e746647a1ce5c10e1708debbf06c45ea7Derek Sollenberger        if (DebugFlags.CACHE_MANAGER) {
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.v(LOGTAG, "getCacheFile for url " + url);
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return result;
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Given a url and its full headers, returns CacheResult if a local cache
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * can be stored. Otherwise returns null. The mimetype is passed in so that
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the function can use the mimetype that will be passed to WebCore which
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * could be different from the mimetype defined in the headers.
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * forceCache is for out-of-package callers to force creation of a
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * CacheResult, and is used to supply surrogate responses for URL
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * interception.
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return CacheResult for a given url
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide - hide createCacheFile since it has a parameter of type headers, which is
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in a hidden package.
459c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     *
460c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     * @deprecated Access to the HTTP cache will be removed in a future release.
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
462c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick    @Deprecated
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CacheResult createCacheFile(String url, int statusCode,
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Headers headers, String mimeType, boolean forceCache) {
465808751fe7ac16bf7224cba284a318695d8093355Steve Block        if (JniUtil.useChromiumHttpStack()) {
466e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // This method is public but hidden. We break functionality.
467808751fe7ac16bf7224cba284a318695d8093355Steve Block            return null;
468808751fe7ac16bf7224cba284a318695d8093355Steve Block        }
469808751fe7ac16bf7224cba284a318695d8093355Steve Block
4708c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        return createCacheFile(url, statusCode, headers, mimeType, 0,
4718c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba                forceCache);
4728c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    }
4738c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
4748c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    static CacheResult createCacheFile(String url, int statusCode,
4758c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            Headers headers, String mimeType, long postIdentifier,
4768c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            boolean forceCache) {
477808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
478808751fe7ac16bf7224cba284a318695d8093355Steve Block
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!forceCache && mDisabled) {
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4838c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        String databaseKey = getDatabaseKey(url, postIdentifier);
4848c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
485105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        // according to the rfc 2616, the 303 response MUST NOT be cached.
486105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (statusCode == 303) {
4873afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba            // remove the saved cache if there is any
4888c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            mDataBase.removeCache(databaseKey);
489105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return null;
490105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        }
491105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
492105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        // like the other browsers, do not cache redirects containing a cookie
493105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        // header.
49467ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        if (isCachableRedirect(statusCode) && !headers.getSetCookie().isEmpty()) {
4953afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba            // remove the saved cache if there is any
4968c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            mDataBase.removeCache(databaseKey);
497105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return null;
498105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        }
499105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CacheResult ret = parseHeaders(statusCode, headers, mimeType);
5013afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba        if (ret == null) {
5023afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba            // this should only happen if the headers has "no-store" in the
5033afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba            // cache-control. remove the saved cache if there is any
5048c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            mDataBase.removeCache(databaseKey);
5053afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba        } else {
5068c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            setupFiles(databaseKey, ret);
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.outStream = new FileOutputStream(ret.outFile);
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (FileNotFoundException e) {
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // This can happen with the system did a purge and our
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // subdirectory has gone, so lets try to create it again
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (createCacheDirectory()) {
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    try {
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ret.outStream = new FileOutputStream(ret.outFile);
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } catch  (FileNotFoundException e2) {
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // We failed to create the file again, so there
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // is something else wrong. Return null.
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return null;
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Failed to create cache directory
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return null;
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ret.mimeType = mimeType;
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ret;
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Save the info of a cache file for a given url to the CacheMap so that it
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * can be reused later
534c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     *
535c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     * @deprecated Access to the HTTP cache will be removed in a future release.
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
537c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick    @Deprecated
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void saveCacheFile(String url, CacheResult cacheRet) {
5398c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        saveCacheFile(url, 0, cacheRet);
5408c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    }
5418c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
5428c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    static void saveCacheFile(String url, long postIdentifier,
5438c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            CacheResult cacheRet) {
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.outStream.close();
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
550808751fe7ac16bf7224cba284a318695d8093355Steve Block        if (JniUtil.useChromiumHttpStack()) {
551e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // This method is exposed in the public API but the API provides no way to obtain a
552e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // new CacheResult object with a non-null output stream ...
553e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // - CacheResult objects returned by getCacheFile() have a null output stream.
554e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // - new CacheResult objects have a null output stream and no setter is provided.
555e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // Since for the Android HTTP stack this method throws a null pointer exception in this
556e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // case, this method is effectively useless from the point of view of the public API.
557e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block
558e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // We should already have thrown an exception above, to maintain 'backward
559e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // compatibility' with the Android HTTP stack.
560e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            assert false;
561808751fe7ac16bf7224cba284a318695d8093355Steve Block        }
562808751fe7ac16bf7224cba284a318695d8093355Steve Block
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!cacheRet.outFile.exists()) {
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the file in the cache directory can be removed by the system
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
56867ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        boolean redirect = isCachableRedirect(cacheRet.httpStatusCode);
569543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark        if (redirect) {
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // location is in database, no need to keep the file
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.contentLength = 0;
572686cf75d5cf447d34961f6217f2ea3ce3e484ac2Cary Clark            cacheRet.localPath = "";
573543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark        }
574543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark        if ((redirect || cacheRet.contentLength == 0)
575543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                && !cacheRet.outFile.delete()) {
576543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark            Log.e(LOGTAG, cacheRet.outFile.getPath() + " delete failed.");
577543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark        }
578543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark        if (cacheRet.contentLength == 0) {
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5828c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        mDataBase.addCache(getDatabaseKey(url, postIdentifier), cacheRet);
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5842e5c150e746647a1ce5c10e1708debbf06c45ea7Derek Sollenberger        if (DebugFlags.CACHE_MANAGER) {
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.v(LOGTAG, "saveCacheFile for url " + url);
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
589998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba    static boolean cleanupCacheFile(CacheResult cacheRet) {
590808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
591808751fe7ac16bf7224cba284a318695d8093355Steve Block
592998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        try {
593998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba            cacheRet.outStream.close();
594998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        } catch (IOException e) {
595998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba            return false;
596998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        }
597998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        return cacheRet.outFile.delete();
598998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba    }
599998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
60167ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * Remove all cache files.
60267ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     *
60367ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * @return Whether the removal succeeded.
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static boolean removeAllCacheFiles() {
606808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
607808751fe7ac16bf7224cba284a318695d8093355Steve Block
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Note, this is called before init() when the database is
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // created or upgraded.
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mBaseDir == null) {
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Init() has not been called yet, so just flag that
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // we need to clear the cache when init() is called.
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mClearCacheOnInit = true;
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6162036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        // delete rows in the cache database
617cedb3a7e5849fd16e939add1ac6f5586467b8c68Kristian Monsen        if (!JniUtil.useChromiumHttpStack())
618cedb3a7e5849fd16e939add1ac6f5586467b8c68Kristian Monsen            WebViewWorker.getHandler().sendEmptyMessage(WebViewWorker.MSG_CLEAR_CACHE);
619cedb3a7e5849fd16e939add1ac6f5586467b8c68Kristian Monsen
6202036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        // delete cache files in a separate thread to not block UI.
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Runnable clearCache = new Runnable() {
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void run() {
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // delete all cache files
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String[] files = mBaseDir.list();
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // if mBaseDir doesn't exist, files can be null.
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (files != null) {
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        for (int i = 0; i < files.length; i++) {
629543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                            File f = new File(mBaseDir, files[i]);
630543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                            if (!f.delete()) {
631543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                                Log.e(LOGTAG, f.getPath() + " delete failed.");
632543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                            }
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } catch (SecurityException e) {
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Ignore SecurityExceptions.
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        };
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new Thread(clearCache).start();
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void trimCacheIfNeeded() {
645808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
646808751fe7ac16bf7224cba284a318695d8093355Steve Block
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDataBase.getCacheTotalSize() > CACHE_THRESHOLD) {
6482036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            List<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT);
6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int size = pathList.size();
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < size; i++) {
651543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                File f = new File(mBaseDir, pathList.get(i));
652543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                if (!f.delete()) {
653543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                    Log.e(LOGTAG, f.getPath() + " delete failed.");
654543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                }
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6562036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            // remove the unreferenced files in the cache directory
6572036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            final List<String> fileList = mDataBase.getAllCacheFileNames();
6582036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            if (fileList == null) return;
6592036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            String[] toDelete = mBaseDir.list(new FilenameFilter() {
6602036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                public boolean accept(File dir, String filename) {
6612036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                    if (fileList.contains(filename)) {
6622036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                        return false;
6632036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                    } else {
6642036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                        return true;
6652036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                    }
6662036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                }
6672036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            });
6682036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            if (toDelete == null) return;
6692036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            size = toDelete.length;
6702036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            for (int i = 0; i < size; i++) {
6712036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                File f = new File(mBaseDir, toDelete[i]);
6722036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                if (!f.delete()) {
6732036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                    Log.e(LOGTAG, f.getPath() + " delete failed.");
6742036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                }
6752036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            }
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6792036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    static void clearCache() {
680808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
681808751fe7ac16bf7224cba284a318695d8093355Steve Block
6822036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        // delete database
6832036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        mDataBase.clearCache();
6842036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    }
6852036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba
68667ba204aa23e7d5a96ad241a1623e44976b51741Steve Block    private static boolean isCachableRedirect(int statusCode) {
687105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (statusCode == 301 || statusCode == 302 || statusCode == 307) {
688105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            // as 303 can't be cached, we do not return true
689105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return true;
690105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        } else {
691105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return false;
692105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        }
693105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
694105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
6958c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    private static String getDatabaseKey(String url, long postIdentifier) {
696808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
697808751fe7ac16bf7224cba284a318695d8093355Steve Block
6988c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        if (postIdentifier == 0) return url;
6998c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        return postIdentifier + url;
7008c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    }
7018c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @SuppressWarnings("deprecation")
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void setupFiles(String url, CacheResult cacheRet) {
704808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
705808751fe7ac16bf7224cba284a318695d8093355Steve Block
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (true) {
7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Note: SHA1 is much stronger hash. But the cost of setupFiles() is
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // 3.2% cpu time for a fresh load of nytimes.com. While a simple
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // String.hashCode() is only 0.6%. If adding the collision resolving
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // to String.hashCode(), it makes the cpu time to be 1.6% for a
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // fresh load, but 5.3% for the worst case where all the files
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // already exist in the file system, but database is gone. So it
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // needs to resolve collision for every file at least once.
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int hashCode = url.hashCode();
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            StringBuffer ret = new StringBuffer(8);
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            appendAsHex(hashCode, ret);
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String path = ret.toString();
7189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File file = new File(mBaseDir, path);
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (true) {
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                boolean checkOldPath = true;
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Check hash collision. If the hash file doesn't exist, just
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // continue. There is a chance that the old cache file is not
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // same as the hash file. As mDataBase.getCache() is more
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // expansive than "leak" a file until clear cache, don't bother.
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // If the hash file exists, make sure that it is same as the
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // cache file. If it is not, resolve the collision.
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (file.exists()) {
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (checkOldPath) {
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        CacheResult oldResult = mDataBase.getCache(url);
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (oldResult != null && oldResult.contentLength > 0) {
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (path.equals(oldResult.localPath)) {
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                path = oldResult.localPath;
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            } else {
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                path = oldResult.localPath;
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                file = new File(mBaseDir, path);
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            break;
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        checkOldPath = false;
7409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret = new StringBuffer(8);
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    appendAsHex(++hashCode, ret);
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    path = ret.toString();
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    file = new File(mBaseDir, path);
7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.localPath = path;
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.outFile = file;
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // get hash in byte[]
7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Digest digest = new SHA1Digest();
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int digestLen = digest.getDigestSize();
7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            byte[] hash = new byte[digestLen];
7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int urlLen = url.length();
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            byte[] data = new byte[urlLen];
7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            url.getBytes(0, urlLen, data, 0);
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            digest.update(data, 0, urlLen);
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            digest.doFinal(hash, 0);
7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // convert byte[] to hex String
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            StringBuffer result = new StringBuffer(2 * digestLen);
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < digestLen; i = i + 4) {
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int h = (0x00ff & hash[i]) << 24 | (0x00ff & hash[i + 1]) << 16
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        | (0x00ff & hash[i + 2]) << 8 | (0x00ff & hash[i + 3]);
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                appendAsHex(h, result);
7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.localPath = result.toString();
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.outFile = new File(mBaseDir, cacheRet.localPath);
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void appendAsHex(int i, StringBuffer ret) {
772808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
773808751fe7ac16bf7224cba284a318695d8093355Steve Block
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String hex = Integer.toHexString(i);
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch (hex.length()) {
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 1:
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("0000000");
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 2:
7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("000000");
7819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
7829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 3:
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("00000");
7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
7859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 4:
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("0000");
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 5:
7899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("000");
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 6:
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("00");
7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 7:
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("0");
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ret.append(hex);
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static CacheResult parseHeaders(int statusCode, Headers headers,
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String mimeType) {
803808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
804808751fe7ac16bf7224cba284a318695d8093355Steve Block
805998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        // if the contentLength is already larger than CACHE_MAX_SIZE, skip it
806998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        if (headers.getContentLength() > CACHE_MAX_SIZE) return null;
807998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba
808a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // The HTML 5 spec, section 6.9.4, step 7.3 of the application cache
809a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // process states that HTTP caching rules are ignored for the
810a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // purposes of the application cache download process.
811a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // At this point we can't tell that if a file is part of this process,
812a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // except for the manifest, which has its own mimeType.
813a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // TODO: work out a way to distinguish all responses that are part of
814a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // the application download process and skip them.
815a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        if (MANIFEST_MIME.equals(mimeType)) return null;
816a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // TODO: if authenticated or secure, return null
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CacheResult ret = new CacheResult();
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ret.httpStatusCode = statusCode;
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8217a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block        ret.location = headers.getLocation();
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ret.expires = -1;
824e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba        ret.expiresString = headers.getExpires();
825e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba        if (ret.expiresString != null) {
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
8277cfa90fee54f44831ac492891d1c123601c2a262Jesse Wilson                ret.expires = AndroidHttpClient.parseDate(ret.expiresString);
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalArgumentException ex) {
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Take care of the special "-1" and "0" cases
830e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba                if ("-1".equals(ret.expiresString)
831e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba                        || "0".equals(ret.expiresString)) {
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // make it expired, but can be used for history navigation
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = 0;
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
835e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba                    Log.e(LOGTAG, "illegal expires: " + ret.expiresString);
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8407a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block        ret.contentdisposition = headers.getContentDisposition();
8410b956e1353a691674cb22c899c5a444b92532b60Grace Kloba
8427a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block        ret.crossDomain = headers.getXPermittedCrossDomainPolicies();
84360708a75120c4469dc2683485301ff9ee3b022e0Leon Clarke
8447865fa97244d2f33d2a9c9ec359b475d6597e994Grace Kloba        // lastModified and etag may be set back to http header. So they can't
8457865fa97244d2f33d2a9c9ec359b475d6597e994Grace Kloba        // be empty string.
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String lastModified = headers.getLastModified();
8477865fa97244d2f33d2a9c9ec359b475d6597e994Grace Kloba        if (lastModified != null && lastModified.length() > 0) {
8487865fa97244d2f33d2a9c9ec359b475d6597e994Grace Kloba            ret.lastModified = lastModified;
8497865fa97244d2f33d2a9c9ec359b475d6597e994Grace Kloba        }
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String etag = headers.getEtag();
8527a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block        if (etag != null && etag.length() > 0) {
8537a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block            ret.etag = etag;
8547a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block        }
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String cacheControl = headers.getCacheControl();
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (cacheControl != null) {
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String[] controls = cacheControl.toLowerCase().split("[ ,;]");
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < controls.length; i++) {
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (NO_STORE.equals(controls[i])) {
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return null;
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // According to the spec, 'no-cache' means that the content
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // must be re-validated on every load. It does not mean that
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // the content can not be cached. set to expire 0 means it
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // can only be used in CACHE_MODE_CACHE_ONLY case
86752cf58a2a47e4dc975314fab44783c7e4654ca6dGrace Kloba                if (NO_CACHE.equals(controls[i])) {
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = 0;
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (controls[i].startsWith(MAX_AGE)) {
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int separator = controls[i].indexOf('=');
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (separator < 0) {
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        separator = controls[i].indexOf(':');
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (separator > 0) {
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        String s = controls[i].substring(separator + 1);
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        try {
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            long sec = Long.parseLong(s);
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (sec >= 0) {
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                ret.expires = System.currentTimeMillis() + 1000
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        * sec;
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } catch (NumberFormatException ex) {
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if ("1d".equals(s)) {
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                // Take care of the special "1d" case
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                ret.expires = System.currentTimeMillis() + 86400000; // 24*60*60*1000
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            } else {
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                Log.e(LOGTAG, "exception in parseHeaders for "
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        + "max-age:"
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        + controls[i].substring(separator + 1));
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                ret.expires = 0;
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // According to RFC 2616 section 14.32:
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // client had sent "Cache-Control: no-cache"
9019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (NO_CACHE.equals(headers.getPragma())) {
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ret.expires = 0;
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // According to RFC 2616 section 13.2.4, if an expiration has not been
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // explicitly defined a heuristic to set an expiration may be used.
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (ret.expires == -1) {
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ret.httpStatusCode == 301) {
9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // If it is a permanent redirect, and it did not have an
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // explicit cache directive, then it never expires
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.expires = Long.MAX_VALUE;
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (ret.httpStatusCode == 302 || ret.httpStatusCode == 307) {
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // If it is temporary redirect, expires
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.expires = 0;
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (ret.lastModified == null) {
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // When we have no last-modified, then expire the content with
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // in 24hrs as, according to the RFC, longer time requires a
9189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // warning 113 to be added to the response.
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Only add the default expiration for non-html markup. Some
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // sites like news.google.com have no cache directives.
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!mimeType.startsWith("text/html")) {
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = System.currentTimeMillis() + 86400000; // 24*60*60*1000
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Setting a expires as zero will cache the result for
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // forward/back nav.
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = 0;
9289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // If we have a last-modified value, we could use it to set the
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // expiration. Suggestion from RFC is 10% of time since
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // last-modified. As we are on mobile, loads are expensive,
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // increasing this to 20%.
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // 24 * 60 * 60 * 1000
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                long lastmod = System.currentTimeMillis() + 86400000;
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
9387cfa90fee54f44831ac492891d1c123601c2a262Jesse Wilson                    lastmod = AndroidHttpClient.parseDate(ret.lastModified);
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } catch (IllegalArgumentException ex) {
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Log.e(LOGTAG, "illegal lastModified: " + ret.lastModified);
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                long difference = System.currentTimeMillis() - lastmod;
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (difference > 0) {
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = System.currentTimeMillis() + difference / 5;
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // last modified is in the future, expire the content
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // on the last modified
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = lastmod;
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ret;
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
955e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block
956e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block    private static native CacheResult nativeGetCacheResult(String url);
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
958