CacheManager.java revision f2bfe2bc05322a754c5a5d4e16ca182303ca72aa
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.content.Context;
20import android.net.http.Headers;
21import android.util.Log;
22
23import java.io.File;
24import java.io.FileInputStream;
25import java.io.FileNotFoundException;
26import java.io.IOException;
27import java.io.InputStream;
28import java.io.OutputStream;
29import java.util.Map;
30
31
32/**
33 * Manages the HTTP cache used by an application's {@link WebView} instances.
34 * @deprecated Access to the HTTP cache will be removed in a future release.
35 */
36// The class CacheManager provides the persistent cache of content that is
37// received over the network. The component handles parsing of HTTP headers and
38// utilizes the relevant cache headers to determine if the content should be
39// stored and if so, how long it is valid for. Network requests are provided to
40// this component and if they can not be resolved by the cache, the HTTP headers
41// are attached, as appropriate, to the request for revalidation of content. The
42// class also manages the cache size.
43//
44// CacheManager may only be used if your activity contains a WebView.
45@Deprecated
46public final class CacheManager {
47
48    private static final String LOGTAG = "cache";
49
50    static final String HEADER_KEY_IFMODIFIEDSINCE = "if-modified-since";
51    static final String HEADER_KEY_IFNONEMATCH = "if-none-match";
52
53    private static File mBaseDir;
54
55    /**
56     * Represents a resource stored in the HTTP cache. Instances of this class
57     * can be obtained by calling
58     * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>))}.
59     * @deprecated Access to the HTTP cache will be removed in a future release.
60     */
61    @Deprecated
62    public static class CacheResult {
63        // these fields are saved to the database
64        int httpStatusCode;
65        long contentLength;
66        long expires;
67        String expiresString;
68        String localPath;
69        String lastModified;
70        String etag;
71        String mimeType;
72        String location;
73        String encoding;
74        String contentdisposition;
75        String crossDomain;
76
77        // these fields are NOT saved to the database
78        InputStream inStream;
79        OutputStream outStream;
80        File outFile;
81
82        /**
83         * Gets the status code of this cache entry.
84         * @return The status code of this cache entry
85         */
86        public int getHttpStatusCode() {
87            return httpStatusCode;
88        }
89
90        /**
91         * Gets the content length of this cache entry.
92         * @return The content length of this cache entry
93         */
94        public long getContentLength() {
95            return contentLength;
96        }
97
98        /**
99         * Gets the path of the file used to store the content of this cache
100         * entry, relative to the base directory of the cache. See
101         * {@link CacheManager#getCacheFileBaseDir CacheManager.getCacheFileBaseDir()}.
102         * @return The path of the file used to store this cache entry
103         */
104        public String getLocalPath() {
105            return localPath;
106        }
107
108        /**
109         * Gets the expiry date of this cache entry, expressed in milliseconds
110         * since midnight, January 1, 1970 UTC.
111         * @return The expiry date of this cache entry
112         */
113        public long getExpires() {
114            return expires;
115        }
116
117        /**
118         * Gets the expiry date of this cache entry, expressed as a string.
119         * @return The expiry date of this cache entry
120         *
121         */
122        public String getExpiresString() {
123            return expiresString;
124        }
125
126        /**
127         * Gets the date at which this cache entry was last modified, expressed
128         * as a string.
129         * @return The date at which this cache entry was last modified
130         */
131        public String getLastModified() {
132            return lastModified;
133        }
134
135        /**
136         * Gets the entity tag of this cache entry.
137         * @return The entity tag of this cache entry
138         */
139        public String getETag() {
140            return etag;
141        }
142
143        /**
144         * Gets the MIME type of this cache entry.
145         * @return The MIME type of this cache entry
146         */
147        public String getMimeType() {
148            return mimeType;
149        }
150
151        /**
152         * Gets the value of the HTTP 'Location' header with which this cache
153         * entry was received.
154         * @return The HTTP 'Location' header for this cache entry
155         */
156        public String getLocation() {
157            return location;
158        }
159
160        /**
161         * Gets the encoding of this cache entry.
162         * @return The encoding of this cache entry
163         */
164        public String getEncoding() {
165            return encoding;
166        }
167
168        /**
169         * Gets the value of the HTTP 'Content-Disposition' header with which
170         * this cache entry was received.
171         * @return The HTTP 'Content-Disposition' header for this cache entry
172         *
173         */
174        public String getContentDisposition() {
175            return contentdisposition;
176        }
177
178        /**
179         * Gets the input stream to the content of this cache entry, to allow
180         * content to be read. See
181         * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>)}.
182         * @return An input stream to the content of this cache entry
183         */
184        public InputStream getInputStream() {
185            return inStream;
186        }
187
188        /**
189         * Gets an output stream to the content of this cache entry, to allow
190         * content to be written. See
191         * {@link CacheManager#saveCacheFile CacheManager.saveCacheFile(String, CacheResult)}.
192         * @return An output stream to the content of this cache entry
193         */
194        // Note that this is always null for objects returned by getCacheFile()!
195        public OutputStream getOutputStream() {
196            return outStream;
197        }
198
199
200        /**
201         * Sets an input stream to the content of this cache entry.
202         * @param stream An input stream to the content of this cache entry
203         */
204        public void setInputStream(InputStream stream) {
205            this.inStream = stream;
206        }
207
208        /**
209         * Sets the encoding of this cache entry.
210         * @param encoding The encoding of this cache entry
211         */
212        public void setEncoding(String encoding) {
213            this.encoding = encoding;
214        }
215
216        /**
217         * @hide
218         */
219        public void setContentLength(long contentLength) {
220            this.contentLength = contentLength;
221        }
222    }
223
224    /**
225     * Initializes the HTTP cache. This method must be called before any
226     * CacheManager methods are used. Note that this is called automatically
227     * when a {@link WebView} is created.
228     * @param context The application context
229     */
230    static void init(Context context) {
231        // This isn't actually where the real cache lives, but where we put files for the
232        // purpose of getCacheFile().
233        mBaseDir = new File(context.getCacheDir(), "webviewCacheChromiumStaging");
234        if (!mBaseDir.exists()) {
235            mBaseDir.mkdirs();
236        }
237    }
238
239    /**
240     * Gets the base directory in which the files used to store the contents of
241     * cache entries are placed. See
242     * {@link CacheManager.CacheResult#getLocalPath CacheManager.CacheResult.getLocalPath()}.
243     * @return The base directory of the cache
244     * @deprecated Access to the HTTP cache will be removed in a future release.
245     */
246    @Deprecated
247    public static File getCacheFileBaseDir() {
248        return mBaseDir;
249    }
250
251    /**
252     * Gets whether the HTTP cache is disabled.
253     * @return True if the HTTP cache is disabled
254     * @deprecated Access to the HTTP cache will be removed in a future release.
255     */
256    @Deprecated
257    public static boolean cacheDisabled() {
258        return false;
259    }
260
261    /**
262     * Starts a cache transaction. Returns true if this is the only running
263     * transaction. Otherwise, this transaction is nested inside currently
264     * running transactions and false is returned.
265     * @return True if this is the only running transaction
266     * @deprecated This method no longer has any effect and always returns false
267     */
268    @Deprecated
269    public static boolean startCacheTransaction() {
270        return false;
271    }
272
273    /**
274     * Ends the innermost cache transaction and returns whether this was the
275     * only running transaction.
276     * @return True if this was the only running transaction
277     * @deprecated This method no longer has any effect and always returns false
278     */
279    @Deprecated
280    public static boolean endCacheTransaction() {
281        return false;
282    }
283
284    /**
285     * Gets the cache entry for the specified URL, or null if none is found.
286     * If a non-null value is provided for the HTTP headers map, and the cache
287     * entry needs validation, appropriate headers will be added to the map.
288     * The input stream of the CacheEntry object should be closed by the caller
289     * when access to the underlying file is no longer required.
290     * @param url The URL for which a cache entry is requested
291     * @param headers A map from HTTP header name to value, to be populated
292     *                for the returned cache entry
293     * @return The cache entry for the specified URL
294     * @deprecated Access to the HTTP cache will be removed in a future release.
295     */
296    @Deprecated
297    public static CacheResult getCacheFile(String url,
298            Map<String, String> headers) {
299        return getCacheFile(url, 0, headers);
300    }
301
302    static CacheResult getCacheFile(String url, long postIdentifier,
303            Map<String, String> headers) {
304        CacheResult result = nativeGetCacheResult(url);
305        if (result == null) {
306            return null;
307        }
308        // A temporary local file will have been created native side and localPath set
309        // appropriately.
310        File src = new File(mBaseDir, result.localPath);
311        try {
312            // Open the file here so that even if it is deleted, the content
313            // is still readable by the caller until close() is called.
314            result.inStream = new FileInputStream(src);
315        } catch (FileNotFoundException e) {
316            Log.v(LOGTAG, "getCacheFile(): Failed to open file: " + e);
317            // TODO: The files in the cache directory can be removed by the
318            // system. If it is gone, what should we do?
319            return null;
320        }
321
322        // A null value for headers is used by CACHE_MODE_CACHE_ONLY to imply
323        // that we should provide the cache result even if it is expired.
324        // Note that a negative expires value means a time in the far future.
325        if (headers != null && result.expires >= 0
326                && result.expires <= System.currentTimeMillis()) {
327            if (result.lastModified == null && result.etag == null) {
328                return null;
329            }
330            // Return HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE
331            // for requesting validation.
332            if (result.etag != null) {
333                headers.put(HEADER_KEY_IFNONEMATCH, result.etag);
334            }
335            if (result.lastModified != null) {
336                headers.put(HEADER_KEY_IFMODIFIEDSINCE, result.lastModified);
337            }
338        }
339
340        if (DebugFlags.CACHE_MANAGER) {
341            Log.v(LOGTAG, "getCacheFile for url " + url);
342        }
343
344        return result;
345    }
346
347    /**
348     * Given a url and its full headers, returns CacheResult if a local cache
349     * can be stored. Otherwise returns null. The mimetype is passed in so that
350     * the function can use the mimetype that will be passed to WebCore which
351     * could be different from the mimetype defined in the headers.
352     * forceCache is for out-of-package callers to force creation of a
353     * CacheResult, and is used to supply surrogate responses for URL
354     * interception.
355     * @return CacheResult for a given url
356     */
357    static CacheResult createCacheFile(String url, int statusCode,
358            Headers headers, String mimeType, boolean forceCache) {
359        // This method is public but hidden. We break functionality.
360        return null;
361    }
362
363    /**
364     * Adds a cache entry to the HTTP cache for the specicifed URL. Also closes
365     * the cache entry's output stream.
366     * @param url The URL for which the cache entry should be added
367     * @param cacheResult The cache entry to add
368     * @deprecated Access to the HTTP cache will be removed in a future release.
369     */
370    @Deprecated
371    public static void saveCacheFile(String url, CacheResult cacheResult) {
372        saveCacheFile(url, 0, cacheResult);
373    }
374
375    static void saveCacheFile(String url, long postIdentifier,
376            CacheResult cacheRet) {
377        try {
378            cacheRet.outStream.close();
379        } catch (IOException e) {
380            return;
381        }
382
383        // This method is exposed in the public API but the API provides no
384        // way to obtain a new CacheResult object with a non-null output
385        // stream ...
386        // - CacheResult objects returned by getCacheFile() have a null
387        //   output stream.
388        // - new CacheResult objects have a null output stream and no
389        //   setter is provided.
390        // Since this method throws a null pointer exception in this case,
391        // it is effectively useless from the point of view of the public
392        // API.
393        //
394        // With the Chromium HTTP stack we continue to throw the same
395        // exception for 'backwards compatibility' with the Android HTTP
396        // stack.
397        //
398        // This method is not used from within this package, and for public API
399        // use, we should already have thrown an exception above.
400        assert false;
401    }
402
403    /**
404     * Remove all cache files.
405     *
406     * @return Whether the removal succeeded.
407     */
408    static boolean removeAllCacheFiles() {
409        // delete cache files in a separate thread to not block UI.
410        final Runnable clearCache = new Runnable() {
411            public void run() {
412                // delete all cache files
413                try {
414                    String[] files = mBaseDir.list();
415                    // if mBaseDir doesn't exist, files can be null.
416                    if (files != null) {
417                        for (int i = 0; i < files.length; i++) {
418                            File f = new File(mBaseDir, files[i]);
419                            if (!f.delete()) {
420                                Log.e(LOGTAG, f.getPath() + " delete failed.");
421                            }
422                        }
423                    }
424                } catch (SecurityException e) {
425                    // Ignore SecurityExceptions.
426                }
427            }
428        };
429        new Thread(clearCache).start();
430        return true;
431    }
432
433    private static native CacheResult nativeGetCacheResult(String url);
434}
435