CacheManager.java revision 303bc083c5a158ff240be658ac30d201cad56a18
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
364140faeebbfa23d56068c1862b2913fb62145f4fBrian Carlstromimport com.android.org.bouncycastle.crypto.Digest;
374140faeebbfa23d56068c1862b2913fb62145f4fBrian Carlstromimport com.android.org.bouncycastle.crypto.digests.SHA1Digest;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
40303bc083c5a158ff240be658ac30d201cad56a18Steve Block * Manages the HTTP cache used by an application's {@link WebView} instances.
41c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick * @deprecated Access to the HTTP cache will be removed in a future release.
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
43303bc083c5a158ff240be658ac30d201cad56a18Steve Block// The class CacheManager provides the persistent cache of content that is
44303bc083c5a158ff240be658ac30d201cad56a18Steve Block// received over the network. The component handles parsing of HTTP headers and
45303bc083c5a158ff240be658ac30d201cad56a18Steve Block// utilizes the relevant cache headers to determine if the content should be
46303bc083c5a158ff240be658ac30d201cad56a18Steve Block// stored and if so, how long it is valid for. Network requests are provided to
47303bc083c5a158ff240be658ac30d201cad56a18Steve Block// this component and if they can not be resolved by the cache, the HTTP headers
48303bc083c5a158ff240be658ac30d201cad56a18Steve Block// are attached, as appropriate, to the request for revalidation of content. The
49303bc083c5a158ff240be658ac30d201cad56a18Steve Block// class also manages the cache size.
50303bc083c5a158ff240be658ac30d201cad56a18Steve Block//
51303bc083c5a158ff240be658ac30d201cad56a18Steve Block// CacheManager may only be used if your activity contains a WebView.
52c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick@Deprecated
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic final class CacheManager {
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String LOGTAG = "cache";
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final String HEADER_KEY_IFMODIFIEDSINCE = "if-modified-since";
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final String HEADER_KEY_IFNONEMATCH = "if-none-match";
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String NO_STORE = "no-store";
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String NO_CACHE = "no-cache";
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String MAX_AGE = "max-age";
63a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu    private static final String MANIFEST_MIME = "text/cache-manifest";
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static long CACHE_THRESHOLD = 6 * 1024 * 1024;
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static long CACHE_TRIM_AMOUNT = 2 * 1024 * 1024;
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
68998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba    // Limit the maximum cache file size to half of the normal capacity
69998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba    static long CACHE_MAX_SIZE = (CACHE_THRESHOLD - CACHE_TRIM_AMOUNT) / 2;
70998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean mDisabled;
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Reference count the enable/disable transaction
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static int mRefCount;
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // trimCacheIfNeeded() is called when a page is fully loaded. But JavaScript
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // can load the content, e.g. in a slideshow, continuously, so we need to
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // trim the cache on a timer base too. endCacheTransaction() is called on a
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // timer base. We share the same timer with less frequent update.
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static int mTrimCacheCount = 0;
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int TRIM_CACHE_INTERVAL = 5;
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static WebViewDatabase mDataBase;
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static File mBaseDir;
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Flag to clear the cache when the CacheManager is initialized
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean mClearCacheOnInit = false;
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
89f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch    /**
90303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * Represents a resource stored in the HTTP cache. Instances of this class
91303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * can be obtained by calling
92303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>))}.
93c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     * @deprecated Access to the HTTP cache will be removed in a future release.
94f0c443deca49d597c8268ef3b0f7198976073241Ben Murdoch     */
95c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick    @Deprecated
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static class CacheResult {
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // these fields are saved to the database
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int httpStatusCode;
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long contentLength;
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        long expires;
101e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba        String expiresString;
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String localPath;
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String lastModified;
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String etag;
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String mimeType;
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String location;
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String encoding;
1080b956e1353a691674cb22c899c5a444b92532b60Grace Kloba        String contentdisposition;
10960708a75120c4469dc2683485301ff9ee3b022e0Leon Clarke        String crossDomain;
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // these fields are NOT saved to the database
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        InputStream inStream;
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        OutputStream outStream;
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File outFile;
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
116303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
117303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the status code of this cache entry.
118303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return The status code of this cache entry
119303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int getHttpStatusCode() {
1219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return httpStatusCode;
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
124303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
125303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the content length of this cache entry.
126303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return The content length of this cache entry
127303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public long getContentLength() {
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return contentLength;
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
132303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
133303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the path of the file used to store the content of this cache
134303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * entry, relative to the base directory of the cache. See
135303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * {@link CacheManager#getCacheFileBaseDir CacheManager.getCacheFileBaseDir()}.
136303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return The path of the file used to store this cache entry
137303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getLocalPath() {
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return localPath;
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
142303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
143303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the expiry date of this cache entry, expressed in milliseconds
144303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * since midnight, January 1, 1970 UTC.
145303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return The expiry date of this cache entry
146303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public long getExpires() {
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return expires;
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
151303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
152303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the expiry date of this cache entry, expressed as a string.
153303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return The expiry date of this cache entry
154303bc083c5a158ff240be658ac30d201cad56a18Steve Block         *
155303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
156e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba        public String getExpiresString() {
157e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba            return expiresString;
158e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba        }
159e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba
160303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
161303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the date at which this cache entry was last modified, expressed
162303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * as a string.
163303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return The date at which this cache entry was last modified
164303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getLastModified() {
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return lastModified;
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
169303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
170303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the entity tag of this cache entry.
171303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return The entity tag of this cache entry
172303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getETag() {
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return etag;
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
177303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
178303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the MIME type of this cache entry.
179303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return The MIME type of this cache entry
180303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getMimeType() {
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return mimeType;
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
185303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
186303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the value of the HTTP 'Location' header with which this cache
187303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * entry was received.
188303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return The HTTP 'Location' header for this cache entry
189303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getLocation() {
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return location;
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
194303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
195303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the encoding of this cache entry.
196303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return The encoding of this cache entry
197303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public String getEncoding() {
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return encoding;
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
202303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
203303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the value of the HTTP 'Content-Disposition' header with which
204303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * this cache entry was received.
205303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return The HTTP 'Content-Disposition' header for this cache entry
206303bc083c5a158ff240be658ac30d201cad56a18Steve Block         *
207303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
2080b956e1353a691674cb22c899c5a444b92532b60Grace Kloba        public String getContentDisposition() {
2090b956e1353a691674cb22c899c5a444b92532b60Grace Kloba            return contentdisposition;
2100b956e1353a691674cb22c899c5a444b92532b60Grace Kloba        }
2110b956e1353a691674cb22c899c5a444b92532b60Grace Kloba
212303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
213303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets the input stream to the content of this cache entry, to allow
214303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * content to be read. See
215303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>)}.
216303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return An input stream to the content of this cache entry
217303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public InputStream getInputStream() {
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return inStream;
2209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
222303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
223303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Gets an output stream to the content of this cache entry, to allow
224303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * content to be written. See
225303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * {@link CacheManager#saveCacheFile CacheManager.saveCacheFile(String, CacheResult)}.
226303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @return An output stream to the content of this cache entry
227303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
228303bc083c5a158ff240be658ac30d201cad56a18Steve Block        // Note that this is always null for objects returned by getCacheFile()!
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public OutputStream getOutputStream() {
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return outStream;
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
233303bc083c5a158ff240be658ac30d201cad56a18Steve Block
234303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
235303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Sets an input stream to the content of this cache entry.
236303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @param stream An input stream to the content of this cache entry
237303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setInputStream(InputStream stream) {
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.inStream = stream;
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
242303bc083c5a158ff240be658ac30d201cad56a18Steve Block        /**
243303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * Sets the encoding of this cache entry.
244303bc083c5a158ff240be658ac30d201cad56a18Steve Block         * @param encoding The encoding of this cache entry
245303bc083c5a158ff240be658ac30d201cad56a18Steve Block         */
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public void setEncoding(String encoding) {
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            this.encoding = encoding;
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
249b67529b905440e2ba550742773b927abad882c19Iain Merrick
250b67529b905440e2ba550742773b927abad882c19Iain Merrick        /**
251b67529b905440e2ba550742773b927abad882c19Iain Merrick         * @hide
252b67529b905440e2ba550742773b927abad882c19Iain Merrick         */
253b67529b905440e2ba550742773b927abad882c19Iain Merrick        public void setContentLength(long contentLength) {
254b67529b905440e2ba550742773b927abad882c19Iain Merrick            this.contentLength = contentLength;
255b67529b905440e2ba550742773b927abad882c19Iain Merrick        }
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
259303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * Initializes the HTTP cache. This method must be called before any
260303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * CacheManager methods are used. Note that this is called automatically
261303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * when a {@link WebView} is created.
262303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * @param context The application context
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void init(Context context) {
265808751fe7ac16bf7224cba284a318695d8093355Steve Block        if (JniUtil.useChromiumHttpStack()) {
266e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // This isn't actually where the real cache lives, but where we put files for the
267e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // purpose of getCacheFile().
268e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            mBaseDir = new File(context.getCacheDir(), "webviewCacheChromiumStaging");
269e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            if (!mBaseDir.exists()) {
270e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block                mBaseDir.mkdirs();
271e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            }
272808751fe7ac16bf7224cba284a318695d8093355Steve Block            return;
273808751fe7ac16bf7224cba284a318695d8093355Steve Block        }
274808751fe7ac16bf7224cba284a318695d8093355Steve Block
27501d0fbfa683012623f030ec75a63e1a9fabcb916Romain Guy        mDataBase = WebViewDatabase.getInstance(context.getApplicationContext());
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mBaseDir = new File(context.getCacheDir(), "webviewCache");
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (createCacheDirectory() && mClearCacheOnInit) {
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            removeAllCacheFiles();
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mClearCacheOnInit = false;
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
28267ba204aa23e7d5a96ad241a1623e44976b51741Steve Block
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Create the cache directory if it does not already exist.
28567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     *
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if the cache directory didn't exist and was created.
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static private boolean createCacheDirectory() {
289808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
290808751fe7ac16bf7224cba284a318695d8093355Steve Block
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!mBaseDir.exists()) {
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if(!mBaseDir.mkdirs()) {
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.w(LOGTAG, "Unable to create webviewCache directory");
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return false;
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            FileUtils.setPermissions(
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mBaseDir.toString(),
29811c1a0be5d8332e662ccc00f4cb66d821e79c8dfJohn Reck                    FileUtils.S_IRWXU | FileUtils.S_IRWXG,
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    -1, -1);
30067ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            // If we did create the directory, we need to flush
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the cache database. The directory could be recreated
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // because the system flushed all the data/cache directories
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // to free up disk space.
3042036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            // delete rows in the cache database
3052036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            WebViewWorker.getHandler().sendEmptyMessage(
3062036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                    WebViewWorker.MSG_CLEAR_CACHE);
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
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
313303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * Gets the base directory in which the files used to store the contents of
314303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * cache entries are placed. See
315303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * {@link CacheManager.CacheResult#getLocalPath CacheManager.CacheResult.getLocalPath()}.
316303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * @return The base directory of the cache
317c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     * @deprecated Access to the HTTP cache will be removed in a future release.
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
319c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick    @Deprecated
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static File getCacheFileBaseDir() {
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mBaseDir;
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
325303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * Sets whether the HTTP cache should be disabled.
32667ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * @param disabled Whether the cache should be disabled
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void setCacheDisabled(boolean disabled) {
329808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
330808751fe7ac16bf7224cba284a318695d8093355Steve Block
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (disabled == mDisabled) {
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        mDisabled = disabled;
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDisabled) {
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            removeAllCacheFiles();
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
341303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * Gets whether the HTTP cache is disabled.
342303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * @return True if the HTTP cache is disabled
343c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     * @deprecated Access to the HTTP cache will be removed in a future release.
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
345c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick    @Deprecated
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static boolean cacheDisabled() {
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDisabled;
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3502036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // only called from WebViewWorkerThread
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // make sure to call enableTransaction/disableTransaction in pair
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static boolean enableTransaction() {
353808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
354808751fe7ac16bf7224cba284a318695d8093355Steve Block
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (++mRefCount == 1) {
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDataBase.startCacheTransaction();
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3622036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // only called from WebViewWorkerThread
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // make sure to call enableTransaction/disableTransaction in pair
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static boolean disableTransaction() {
365808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
366808751fe7ac16bf7224cba284a318695d8093355Steve Block
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (--mRefCount == 0) {
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mDataBase.endCacheTransaction();
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return false;
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3742036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // only called from WebViewWorkerThread
3752036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // make sure to call startTransaction/endTransaction in pair
3762036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    static boolean startTransaction() {
377808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
378808751fe7ac16bf7224cba284a318695d8093355Steve Block
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return mDataBase.startCacheTransaction();
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3822036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // only called from WebViewWorkerThread
3832036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    // make sure to call startTransaction/endTransaction in pair
3842036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    static boolean endTransaction() {
385808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
386808751fe7ac16bf7224cba284a318695d8093355Steve Block
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean ret = mDataBase.endCacheTransaction();
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (++mTrimCacheCount >= TRIM_CACHE_INTERVAL) {
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mTrimCacheCount = 0;
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            trimCacheIfNeeded();
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ret;
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3952036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    /**
396303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * Starts a cache transaction. Returns true if this is the only running
397303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * transaction. Otherwise, this transaction is nested inside currently
398303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * running transactions and false is returned.
399303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * @return True if this is the only running transaction
400303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * @deprecated This method no longer has any effect and always returns false
4012036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba     */
4022036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    @Deprecated
4032036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    public static boolean startCacheTransaction() {
4042036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        return false;
4052036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    }
4062036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba
4072036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    /**
408303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * Ends the innermost cache transaction and returns whether this was the
409303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * only running transaction.
410303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * @return True if this was the only running transaction
411303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * @deprecated This method no longer has any effect and always returns false
4122036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba     */
4132036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    @Deprecated
4142036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    public static boolean endCacheTransaction() {
4152036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        return false;
4162036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    }
4172036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
419ffefba15e3a011836f037c185d4909a59995ff32Steve Block     * Gets the cache entry for the specified URL, or null if none is found.
420ffefba15e3a011836f037c185d4909a59995ff32Steve Block     * If a non-null value is provided for the HTTP headers map, and the cache
421ffefba15e3a011836f037c185d4909a59995ff32Steve Block     * entry needs validation, appropriate headers will be added to the map.
422ffefba15e3a011836f037c185d4909a59995ff32Steve Block     * The input stream of the CacheEntry object should be closed by the caller
423ffefba15e3a011836f037c185d4909a59995ff32Steve Block     * when access to the underlying file is no longer required.
424ffefba15e3a011836f037c185d4909a59995ff32Steve Block     * @param url The URL for which a cache entry is requested
425ffefba15e3a011836f037c185d4909a59995ff32Steve Block     * @param headers A map from HTTP header name to value, to be populated
426ffefba15e3a011836f037c185d4909a59995ff32Steve Block     *                for the returned cache entry
427ffefba15e3a011836f037c185d4909a59995ff32Steve Block     * @return The cache entry for the specified URL
428c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     * @deprecated Access to the HTTP cache will be removed in a future release.
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
430c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick    @Deprecated
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CacheResult getCacheFile(String url,
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Map<String, String> headers) {
4338c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        return getCacheFile(url, 0, headers);
4348c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    }
4358c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
436ffefba15e3a011836f037c185d4909a59995ff32Steve Block    private static CacheResult getCacheFileChromiumHttpStack(String url) {
437ffefba15e3a011836f037c185d4909a59995ff32Steve Block        assert JniUtil.useChromiumHttpStack();
438ffefba15e3a011836f037c185d4909a59995ff32Steve Block
439ffefba15e3a011836f037c185d4909a59995ff32Steve Block        CacheResult result = nativeGetCacheResult(url);
440ffefba15e3a011836f037c185d4909a59995ff32Steve Block        if (result == null) {
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
443ffefba15e3a011836f037c185d4909a59995ff32Steve Block        // A temporary local file will have been created native side and localPath set
444ffefba15e3a011836f037c185d4909a59995ff32Steve Block        // appropriately.
445ffefba15e3a011836f037c185d4909a59995ff32Steve Block        File src = new File(mBaseDir, result.localPath);
446ffefba15e3a011836f037c185d4909a59995ff32Steve Block        try {
447ffefba15e3a011836f037c185d4909a59995ff32Steve Block            // Open the file here so that even if it is deleted, the content
448ffefba15e3a011836f037c185d4909a59995ff32Steve Block            // is still readable by the caller until close() is called.
449ffefba15e3a011836f037c185d4909a59995ff32Steve Block            result.inStream = new FileInputStream(src);
450ffefba15e3a011836f037c185d4909a59995ff32Steve Block        } catch (FileNotFoundException e) {
451ffefba15e3a011836f037c185d4909a59995ff32Steve Block            Log.v(LOGTAG, "getCacheFile(): Failed to open file: " + e);
452ffefba15e3a011836f037c185d4909a59995ff32Steve Block            // TODO: The files in the cache directory can be removed by the
453ffefba15e3a011836f037c185d4909a59995ff32Steve Block            // system. If it is gone, what should we do?
454ffefba15e3a011836f037c185d4909a59995ff32Steve Block            return null;
455808751fe7ac16bf7224cba284a318695d8093355Steve Block        }
456ffefba15e3a011836f037c185d4909a59995ff32Steve Block        return result;
457ffefba15e3a011836f037c185d4909a59995ff32Steve Block    }
458ffefba15e3a011836f037c185d4909a59995ff32Steve Block
459ffefba15e3a011836f037c185d4909a59995ff32Steve Block    private static CacheResult getCacheFileAndroidHttpStack(String url,
460ffefba15e3a011836f037c185d4909a59995ff32Steve Block            long postIdentifier) {
461ffefba15e3a011836f037c185d4909a59995ff32Steve Block        assert !JniUtil.useChromiumHttpStack();
4628c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
463808751fe7ac16bf7224cba284a318695d8093355Steve Block        String databaseKey = getDatabaseKey(url, postIdentifier);
4648c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        CacheResult result = mDataBase.getCache(databaseKey);
46567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        if (result == null) {
46667ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            return null;
46767ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        }
46867ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        if (result.contentLength == 0) {
46967ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            if (!isCachableRedirect(result.httpStatusCode)) {
47067ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                // This should not happen. If it does, remove it.
47167ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                mDataBase.removeCache(databaseKey);
47267ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                return null;
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
47567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            File src = new File(mBaseDir, result.localPath);
47667ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            try {
47767ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                // Open the file here so that even if it is deleted, the content
47867ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                // is still readable by the caller until close() is called.
47967ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                result.inStream = new FileInputStream(src);
48067ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            } catch (FileNotFoundException e) {
48167ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                // The files in the cache directory can be removed by the
48267ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                // system. If it is gone, clean up the database.
48367ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                mDataBase.removeCache(databaseKey);
48467ba204aa23e7d5a96ad241a1623e44976b51741Steve Block                return null;
48567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            }
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
487ffefba15e3a011836f037c185d4909a59995ff32Steve Block        return result;
488ffefba15e3a011836f037c185d4909a59995ff32Steve Block    }
489ffefba15e3a011836f037c185d4909a59995ff32Steve Block
490ffefba15e3a011836f037c185d4909a59995ff32Steve Block    static CacheResult getCacheFile(String url, long postIdentifier,
491ffefba15e3a011836f037c185d4909a59995ff32Steve Block            Map<String, String> headers) {
492ffefba15e3a011836f037c185d4909a59995ff32Steve Block        if (mDisabled) {
493ffefba15e3a011836f037c185d4909a59995ff32Steve Block            return null;
494ffefba15e3a011836f037c185d4909a59995ff32Steve Block        }
495ffefba15e3a011836f037c185d4909a59995ff32Steve Block
496ffefba15e3a011836f037c185d4909a59995ff32Steve Block        CacheResult result = JniUtil.useChromiumHttpStack() ?
497ffefba15e3a011836f037c185d4909a59995ff32Steve Block                getCacheFileChromiumHttpStack(url) :
498ffefba15e3a011836f037c185d4909a59995ff32Steve Block                getCacheFileAndroidHttpStack(url, postIdentifier);
499ffefba15e3a011836f037c185d4909a59995ff32Steve Block
500ffefba15e3a011836f037c185d4909a59995ff32Steve Block        if (result == null) {
501ffefba15e3a011836f037c185d4909a59995ff32Steve Block            return null;
502ffefba15e3a011836f037c185d4909a59995ff32Steve Block        }
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
50467ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        // A null value for headers is used by CACHE_MODE_CACHE_ONLY to imply
50567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        // that we should provide the cache result even if it is expired.
50667ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        // Note that a negative expires value means a time in the far future.
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (headers != null && result.expires >= 0
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                && result.expires <= System.currentTimeMillis()) {
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (result.lastModified == null && result.etag == null) {
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return null;
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
51267ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            // Return HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE
51367ba204aa23e7d5a96ad241a1623e44976b51741Steve Block            // for requesting validation.
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (result.etag != null) {
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                headers.put(HEADER_KEY_IFNONEMATCH, result.etag);
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (result.lastModified != null) {
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                headers.put(HEADER_KEY_IFMODIFIEDSINCE, result.lastModified);
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5222e5c150e746647a1ce5c10e1708debbf06c45ea7Derek Sollenberger        if (DebugFlags.CACHE_MANAGER) {
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.v(LOGTAG, "getCacheFile for url " + url);
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return result;
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Given a url and its full headers, returns CacheResult if a local cache
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * can be stored. Otherwise returns null. The mimetype is passed in so that
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the function can use the mimetype that will be passed to WebCore which
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * could be different from the mimetype defined in the headers.
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * forceCache is for out-of-package callers to force creation of a
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * CacheResult, and is used to supply surrogate responses for URL
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * interception.
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return CacheResult for a given url
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
539303bc083c5a158ff240be658ac30d201cad56a18Steve Block    static CacheResult createCacheFile(String url, int statusCode,
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Headers headers, String mimeType, boolean forceCache) {
541808751fe7ac16bf7224cba284a318695d8093355Steve Block        if (JniUtil.useChromiumHttpStack()) {
542e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            // This method is public but hidden. We break functionality.
543808751fe7ac16bf7224cba284a318695d8093355Steve Block            return null;
544808751fe7ac16bf7224cba284a318695d8093355Steve Block        }
545808751fe7ac16bf7224cba284a318695d8093355Steve Block
5468c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        return createCacheFile(url, statusCode, headers, mimeType, 0,
5478c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba                forceCache);
5488c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    }
5498c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
5508c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    static CacheResult createCacheFile(String url, int statusCode,
5518c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            Headers headers, String mimeType, long postIdentifier,
5528c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            boolean forceCache) {
553808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
554808751fe7ac16bf7224cba284a318695d8093355Steve Block
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!forceCache && mDisabled) {
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5598c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        String databaseKey = getDatabaseKey(url, postIdentifier);
5608c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
561105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        // according to the rfc 2616, the 303 response MUST NOT be cached.
562105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (statusCode == 303) {
5633afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba            // remove the saved cache if there is any
5648c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            mDataBase.removeCache(databaseKey);
565105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return null;
566105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        }
567105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
568105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        // like the other browsers, do not cache redirects containing a cookie
569105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        // header.
57067ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        if (isCachableRedirect(statusCode) && !headers.getSetCookie().isEmpty()) {
5713afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba            // remove the saved cache if there is any
5728c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            mDataBase.removeCache(databaseKey);
573105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return null;
574105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        }
575105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CacheResult ret = parseHeaders(statusCode, headers, mimeType);
5773afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba        if (ret == null) {
5783afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba            // this should only happen if the headers has "no-store" in the
5793afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba            // cache-control. remove the saved cache if there is any
5808c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            mDataBase.removeCache(databaseKey);
5813afdd56470d6d4dcb20fe0f68ec9e54a167a9d74Grace Kloba        } else {
5828c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            setupFiles(databaseKey, ret);
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.outStream = new FileOutputStream(ret.outFile);
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (FileNotFoundException e) {
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // This can happen with the system did a purge and our
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // subdirectory has gone, so lets try to create it again
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (createCacheDirectory()) {
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    try {
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ret.outStream = new FileOutputStream(ret.outFile);
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } catch  (FileNotFoundException e2) {
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // We failed to create the file again, so there
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        // is something else wrong. Return null.
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        return null;
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Failed to create cache directory
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return null;
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ret.mimeType = mimeType;
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ret;
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
608303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * Adds a cache entry to the HTTP cache for the specicifed URL. Also closes
609303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * the cache entry's output stream.
610303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * @param url The URL for which the cache entry should be added
611303bc083c5a158ff240be658ac30d201cad56a18Steve Block     * @param cacheResult The cache entry to add
612c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick     * @deprecated Access to the HTTP cache will be removed in a future release.
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
614c96235deb9f4d08285f3b1a2c28ea9f771b40f47Iain Merrick    @Deprecated
615303bc083c5a158ff240be658ac30d201cad56a18Steve Block    public static void saveCacheFile(String url, CacheResult cacheResult) {
616303bc083c5a158ff240be658ac30d201cad56a18Steve Block        saveCacheFile(url, 0, cacheResult);
6178c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    }
6188c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
6198c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    static void saveCacheFile(String url, long postIdentifier,
6208c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba            CacheResult cacheRet) {
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.outStream.close();
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
627808751fe7ac16bf7224cba284a318695d8093355Steve Block        if (JniUtil.useChromiumHttpStack()) {
628303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // This method is exposed in the public API but the API provides no
629303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // way to obtain a new CacheResult object with a non-null output
630303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // stream ...
631303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // - CacheResult objects returned by getCacheFile() have a null
632303bc083c5a158ff240be658ac30d201cad56a18Steve Block            //   output stream.
633303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // - new CacheResult objects have a null output stream and no
634303bc083c5a158ff240be658ac30d201cad56a18Steve Block            //   setter is provided.
635303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // Since this method throws a null pointer exception in this case,
636303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // it is effectively useless from the point of view of the public
637303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // API.
638303bc083c5a158ff240be658ac30d201cad56a18Steve Block            //
639303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // With the Chromium HTTP stack we continue to throw the same
640303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // exception for 'backwards compatibility' with the Android HTTP
641303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // stack.
642303bc083c5a158ff240be658ac30d201cad56a18Steve Block            //
643303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // This method is not used from within this package with the
644303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // Chromium HTTP stack, and for public API use, we should already
645303bc083c5a158ff240be658ac30d201cad56a18Steve Block            // have thrown an exception above.
646e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block            assert false;
647303bc083c5a158ff240be658ac30d201cad56a18Steve Block            return;
648808751fe7ac16bf7224cba284a318695d8093355Steve Block        }
649808751fe7ac16bf7224cba284a318695d8093355Steve Block
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!cacheRet.outFile.exists()) {
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // the file in the cache directory can be removed by the system
6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
65567ba204aa23e7d5a96ad241a1623e44976b51741Steve Block        boolean redirect = isCachableRedirect(cacheRet.httpStatusCode);
656543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark        if (redirect) {
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // location is in database, no need to keep the file
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.contentLength = 0;
659686cf75d5cf447d34961f6217f2ea3ce3e484ac2Cary Clark            cacheRet.localPath = "";
660543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark        }
661543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark        if ((redirect || cacheRet.contentLength == 0)
662543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                && !cacheRet.outFile.delete()) {
663543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark            Log.e(LOGTAG, cacheRet.outFile.getPath() + " delete failed.");
664543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark        }
665543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark        if (cacheRet.contentLength == 0) {
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6698c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        mDataBase.addCache(getDatabaseKey(url, postIdentifier), cacheRet);
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6712e5c150e746647a1ce5c10e1708debbf06c45ea7Derek Sollenberger        if (DebugFlags.CACHE_MANAGER) {
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.v(LOGTAG, "saveCacheFile for url " + url);
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
676998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba    static boolean cleanupCacheFile(CacheResult cacheRet) {
677808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
678808751fe7ac16bf7224cba284a318695d8093355Steve Block
679998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        try {
680998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba            cacheRet.outStream.close();
681998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        } catch (IOException e) {
682998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba            return false;
683998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        }
684998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        return cacheRet.outFile.delete();
685998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba    }
686998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
68867ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * Remove all cache files.
68967ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     *
69067ba204aa23e7d5a96ad241a1623e44976b51741Steve Block     * @return Whether the removal succeeded.
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static boolean removeAllCacheFiles() {
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Note, this is called before init() when the database is
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // created or upgraded.
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mBaseDir == null) {
6968beb86c0430a7125bb1617172f34187308b178c0Kristian Monsen            // This method should not be called before init() when using the
6978beb86c0430a7125bb1617172f34187308b178c0Kristian Monsen            // chrome http stack
6988beb86c0430a7125bb1617172f34187308b178c0Kristian Monsen            assert !JniUtil.useChromiumHttpStack();
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Init() has not been called yet, so just flag that
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // we need to clear the cache when init() is called.
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mClearCacheOnInit = true;
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return true;
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7042036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        // delete rows in the cache database
705cedb3a7e5849fd16e939add1ac6f5586467b8c68Kristian Monsen        if (!JniUtil.useChromiumHttpStack())
706cedb3a7e5849fd16e939add1ac6f5586467b8c68Kristian Monsen            WebViewWorker.getHandler().sendEmptyMessage(WebViewWorker.MSG_CLEAR_CACHE);
707cedb3a7e5849fd16e939add1ac6f5586467b8c68Kristian Monsen
7082036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        // delete cache files in a separate thread to not block UI.
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Runnable clearCache = new Runnable() {
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            public void run() {
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // delete all cache files
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    String[] files = mBaseDir.list();
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // if mBaseDir doesn't exist, files can be null.
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (files != null) {
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        for (int i = 0; i < files.length; i++) {
717543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                            File f = new File(mBaseDir, files[i]);
718543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                            if (!f.delete()) {
719543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                                Log.e(LOGTAG, f.getPath() + " delete failed.");
720543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                            }
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } catch (SecurityException e) {
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Ignore SecurityExceptions.
7259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        };
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        new Thread(clearCache).start();
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void trimCacheIfNeeded() {
733808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
734808751fe7ac16bf7224cba284a318695d8093355Steve Block
7359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDataBase.getCacheTotalSize() > CACHE_THRESHOLD) {
7362036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            List<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT);
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int size = pathList.size();
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < size; i++) {
739543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                File f = new File(mBaseDir, pathList.get(i));
740543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                if (!f.delete()) {
741543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                    Log.e(LOGTAG, f.getPath() + " delete failed.");
742543221fc4b9dd16db2c687cd59f1eeea8d89c5a5Cary Clark                }
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7442036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            // remove the unreferenced files in the cache directory
7452036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            final List<String> fileList = mDataBase.getAllCacheFileNames();
7462036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            if (fileList == null) return;
7472036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            String[] toDelete = mBaseDir.list(new FilenameFilter() {
7482036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                public boolean accept(File dir, String filename) {
7492036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                    if (fileList.contains(filename)) {
7502036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                        return false;
7512036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                    } else {
7522036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                        return true;
7532036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                    }
7542036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                }
7552036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            });
7562036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            if (toDelete == null) return;
7572036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            size = toDelete.length;
7582036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            for (int i = 0; i < size; i++) {
7592036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                File f = new File(mBaseDir, toDelete[i]);
7602036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                if (!f.delete()) {
7612036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                    Log.e(LOGTAG, f.getPath() + " delete failed.");
7622036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba                }
7632036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba            }
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7672036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    static void clearCache() {
768808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
769808751fe7ac16bf7224cba284a318695d8093355Steve Block
7702036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        // delete database
7712036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba        mDataBase.clearCache();
7722036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba    }
7732036dbab1726c34953360a7a56d6b9ef1f2aa7ddGrace Kloba
77467ba204aa23e7d5a96ad241a1623e44976b51741Steve Block    private static boolean isCachableRedirect(int statusCode) {
775105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (statusCode == 301 || statusCode == 302 || statusCode == 307) {
776105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            // as 303 can't be cached, we do not return true
777105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return true;
778105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        } else {
779105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            return false;
780105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        }
781105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    }
782105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
7838c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    private static String getDatabaseKey(String url, long postIdentifier) {
784808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
785808751fe7ac16bf7224cba284a318695d8093355Steve Block
7868c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        if (postIdentifier == 0) return url;
7878c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba        return postIdentifier + url;
7888c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba    }
7898c92c39b858ae73a1b08ed698887efa98ced987cGrace Kloba
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @SuppressWarnings("deprecation")
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void setupFiles(String url, CacheResult cacheRet) {
792808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
793808751fe7ac16bf7224cba284a318695d8093355Steve Block
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (true) {
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Note: SHA1 is much stronger hash. But the cost of setupFiles() is
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // 3.2% cpu time for a fresh load of nytimes.com. While a simple
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // String.hashCode() is only 0.6%. If adding the collision resolving
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // to String.hashCode(), it makes the cpu time to be 1.6% for a
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // fresh load, but 5.3% for the worst case where all the files
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // already exist in the file system, but database is gone. So it
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // needs to resolve collision for every file at least once.
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int hashCode = url.hashCode();
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            StringBuffer ret = new StringBuffer(8);
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            appendAsHex(hashCode, ret);
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String path = ret.toString();
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            File file = new File(mBaseDir, path);
8079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (true) {
8089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                boolean checkOldPath = true;
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Check hash collision. If the hash file doesn't exist, just
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // continue. There is a chance that the old cache file is not
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // same as the hash file. As mDataBase.getCache() is more
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // expansive than "leak" a file until clear cache, don't bother.
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // If the hash file exists, make sure that it is same as the
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // cache file. If it is not, resolve the collision.
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                while (file.exists()) {
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (checkOldPath) {
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        CacheResult oldResult = mDataBase.getCache(url);
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (oldResult != null && oldResult.contentLength > 0) {
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (path.equals(oldResult.localPath)) {
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                path = oldResult.localPath;
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            } else {
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                path = oldResult.localPath;
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                file = new File(mBaseDir, path);
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            break;
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        checkOldPath = false;
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret = new StringBuffer(8);
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    appendAsHex(++hashCode, ret);
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    path = ret.toString();
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    file = new File(mBaseDir, path);
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.localPath = path;
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.outFile = file;
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // get hash in byte[]
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Digest digest = new SHA1Digest();
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int digestLen = digest.getDigestSize();
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            byte[] hash = new byte[digestLen];
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int urlLen = url.length();
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            byte[] data = new byte[urlLen];
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            url.getBytes(0, urlLen, data, 0);
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            digest.update(data, 0, urlLen);
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            digest.doFinal(hash, 0);
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // convert byte[] to hex String
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            StringBuffer result = new StringBuffer(2 * digestLen);
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < digestLen; i = i + 4) {
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int h = (0x00ff & hash[i]) << 24 | (0x00ff & hash[i + 1]) << 16
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        | (0x00ff & hash[i + 2]) << 8 | (0x00ff & hash[i + 3]);
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                appendAsHex(h, result);
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.localPath = result.toString();
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cacheRet.outFile = new File(mBaseDir, cacheRet.localPath);
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void appendAsHex(int i, StringBuffer ret) {
860808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
861808751fe7ac16bf7224cba284a318695d8093355Steve Block
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String hex = Integer.toHexString(i);
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch (hex.length()) {
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 1:
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("0000000");
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 2:
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("000000");
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 3:
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("00000");
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 4:
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("0000");
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 5:
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("000");
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 6:
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("00");
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case 7:
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.append("0");
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ret.append(hex);
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static CacheResult parseHeaders(int statusCode, Headers headers,
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String mimeType) {
891808751fe7ac16bf7224cba284a318695d8093355Steve Block        assert !JniUtil.useChromiumHttpStack();
892808751fe7ac16bf7224cba284a318695d8093355Steve Block
893998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        // if the contentLength is already larger than CACHE_MAX_SIZE, skip it
894998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba        if (headers.getContentLength() > CACHE_MAX_SIZE) return null;
895998c05b3b5db124a31da1ac38af0e97bca114122Grace Kloba
896a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // The HTML 5 spec, section 6.9.4, step 7.3 of the application cache
897a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // process states that HTTP caching rules are ignored for the
898a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // purposes of the application cache download process.
899a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // At this point we can't tell that if a file is part of this process,
900a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // except for the manifest, which has its own mimeType.
901a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // TODO: work out a way to distinguish all responses that are part of
902a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        // the application download process and skip them.
903a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu        if (MANIFEST_MIME.equals(mimeType)) return null;
904a1ba11bd78f9540e58516dd742fe4cc8726e262fAndrei Popescu
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // TODO: if authenticated or secure, return null
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CacheResult ret = new CacheResult();
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ret.httpStatusCode = statusCode;
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9097a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block        ret.location = headers.getLocation();
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ret.expires = -1;
912e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba        ret.expiresString = headers.getExpires();
913e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba        if (ret.expiresString != null) {
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
9157cfa90fee54f44831ac492891d1c123601c2a262Jesse Wilson                ret.expires = AndroidHttpClient.parseDate(ret.expiresString);
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalArgumentException ex) {
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Take care of the special "-1" and "0" cases
918e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba                if ("-1".equals(ret.expiresString)
919e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba                        || "0".equals(ret.expiresString)) {
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // make it expired, but can be used for history navigation
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = 0;
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
923e64c5567de20d06ac7ed1f5a01f018991cd40a52Grace Kloba                    Log.e(LOGTAG, "illegal expires: " + ret.expiresString);
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9287a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block        ret.contentdisposition = headers.getContentDisposition();
9290b956e1353a691674cb22c899c5a444b92532b60Grace Kloba
9307a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block        ret.crossDomain = headers.getXPermittedCrossDomainPolicies();
93160708a75120c4469dc2683485301ff9ee3b022e0Leon Clarke
9327865fa97244d2f33d2a9c9ec359b475d6597e994Grace Kloba        // lastModified and etag may be set back to http header. So they can't
9337865fa97244d2f33d2a9c9ec359b475d6597e994Grace Kloba        // be empty string.
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String lastModified = headers.getLastModified();
9357865fa97244d2f33d2a9c9ec359b475d6597e994Grace Kloba        if (lastModified != null && lastModified.length() > 0) {
9367865fa97244d2f33d2a9c9ec359b475d6597e994Grace Kloba            ret.lastModified = lastModified;
9377865fa97244d2f33d2a9c9ec359b475d6597e994Grace Kloba        }
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String etag = headers.getEtag();
9407a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block        if (etag != null && etag.length() > 0) {
9417a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block            ret.etag = etag;
9427a1121511d8b587a3ab9fc971c0e85cef38f3f81Steve Block        }
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String cacheControl = headers.getCacheControl();
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (cacheControl != null) {
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String[] controls = cacheControl.toLowerCase().split("[ ,;]");
947eb0ced7a094df2c73e052a066535f4359b11a92dHenrik Baard            boolean noCache = false;
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < controls.length; i++) {
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (NO_STORE.equals(controls[i])) {
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return null;
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // According to the spec, 'no-cache' means that the content
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // must be re-validated on every load. It does not mean that
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // the content can not be cached. set to expire 0 means it
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // can only be used in CACHE_MODE_CACHE_ONLY case
95652cf58a2a47e4dc975314fab44783c7e4654ca6dGrace Kloba                if (NO_CACHE.equals(controls[i])) {
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = 0;
958eb0ced7a094df2c73e052a066535f4359b11a92dHenrik Baard                    noCache = true;
959eb0ced7a094df2c73e052a066535f4359b11a92dHenrik Baard                // if cache control = no-cache has been received, ignore max-age
960eb0ced7a094df2c73e052a066535f4359b11a92dHenrik Baard                // header, according to http spec:
961eb0ced7a094df2c73e052a066535f4359b11a92dHenrik Baard                // If a request includes the no-cache directive, it SHOULD NOT
962eb0ced7a094df2c73e052a066535f4359b11a92dHenrik Baard                // include min-fresh, max-stale, or max-age.
963eb0ced7a094df2c73e052a066535f4359b11a92dHenrik Baard                } else if (controls[i].startsWith(MAX_AGE) && !noCache) {
9649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    int separator = controls[i].indexOf('=');
9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (separator < 0) {
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        separator = controls[i].indexOf(':');
9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
9689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (separator > 0) {
9699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        String s = controls[i].substring(separator + 1);
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        try {
9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            long sec = Long.parseLong(s);
9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (sec >= 0) {
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                ret.expires = System.currentTimeMillis() + 1000
9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        * sec;
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        } catch (NumberFormatException ex) {
9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if ("1d".equals(s)) {
9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                // Take care of the special "1d" case
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                ret.expires = System.currentTimeMillis() + 86400000; // 24*60*60*1000
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            } else {
9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                Log.e(LOGTAG, "exception in parseHeaders for "
9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        + "max-age:"
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                        + controls[i].substring(separator + 1));
9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                ret.expires = 0;
9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
9869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // According to RFC 2616 section 14.32:
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the
9949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // client had sent "Cache-Control: no-cache"
9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (NO_CACHE.equals(headers.getPragma())) {
9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ret.expires = 0;
9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // According to RFC 2616 section 13.2.4, if an expiration has not been
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // explicitly defined a heuristic to set an expiration may be used.
10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (ret.expires == -1) {
10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (ret.httpStatusCode == 301) {
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // If it is a permanent redirect, and it did not have an
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // explicit cache directive, then it never expires
10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.expires = Long.MAX_VALUE;
10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (ret.httpStatusCode == 302 || ret.httpStatusCode == 307) {
10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // If it is temporary redirect, expires
10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ret.expires = 0;
10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (ret.lastModified == null) {
10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // When we have no last-modified, then expire the content with
10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // in 24hrs as, according to the RFC, longer time requires a
10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // warning 113 to be added to the response.
10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Only add the default expiration for non-html markup. Some
10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // sites like news.google.com have no cache directives.
10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!mimeType.startsWith("text/html")) {
10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = System.currentTimeMillis() + 86400000; // 24*60*60*1000
10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // Setting a expires as zero will cache the result for
10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // forward/back nav.
10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = 0;
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // If we have a last-modified value, we could use it to set the
10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // expiration. Suggestion from RFC is 10% of time since
10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // last-modified. As we are on mobile, loads are expensive,
10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // increasing this to 20%.
10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // 24 * 60 * 60 * 1000
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                long lastmod = System.currentTimeMillis() + 86400000;
10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                try {
10327cfa90fee54f44831ac492891d1c123601c2a262Jesse Wilson                    lastmod = AndroidHttpClient.parseDate(ret.lastModified);
10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } catch (IllegalArgumentException ex) {
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    Log.e(LOGTAG, "illegal lastModified: " + ret.lastModified);
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                long difference = System.currentTimeMillis() - lastmod;
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (difference > 0) {
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = System.currentTimeMillis() + difference / 5;
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // last modified is in the future, expire the content
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // on the last modified
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    ret.expires = lastmod;
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ret;
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1049e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block
1050e8492473a94e827b9a73f1fa4f5741e85c0e832cSteve Block    private static native CacheResult nativeGetCacheResult(String url);
10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1052