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