WebViewDatabase.java revision 0b956e1353a691674cb22c899c5a444b92532b60
1/* 2 * Copyright (C) 2007 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 java.util.ArrayList; 20import java.util.HashMap; 21import java.util.Iterator; 22import java.util.Set; 23import java.util.Map.Entry; 24 25import android.content.ContentValues; 26import android.content.Context; 27import android.database.Cursor; 28import android.database.DatabaseUtils; 29import android.database.sqlite.SQLiteDatabase; 30import android.database.sqlite.SQLiteStatement; 31import android.util.Log; 32import android.webkit.CookieManager.Cookie; 33import android.webkit.CacheManager.CacheResult; 34 35public class WebViewDatabase { 36 private static final String DATABASE_FILE = "webview.db"; 37 private static final String CACHE_DATABASE_FILE = "webviewCache.db"; 38 39 // log tag 40 protected static final String LOGTAG = "webviewdatabase"; 41 42 private static final int DATABASE_VERSION = 9; 43 // 2 -> 3 Modified Cache table to allow cache of redirects 44 // 3 -> 4 Added Oma-Downloads table 45 // 4 -> 5 Modified Cache table to support persistent contentLength 46 // 5 -> 4 Removed Oma-Downoads table 47 // 5 -> 6 Add INDEX for cache table 48 // 6 -> 7 Change cache localPath from int to String 49 // 7 -> 8 Move cache to its own db 50 // 8 -> 9 Store both scheme and host when storing passwords 51 private static final int CACHE_DATABASE_VERSION = 3; 52 // 1 -> 2 Add expires String 53 // 2 -> 3 Add content-disposition 54 55 private static WebViewDatabase mInstance = null; 56 57 private static SQLiteDatabase mDatabase = null; 58 private static SQLiteDatabase mCacheDatabase = null; 59 60 // synchronize locks 61 private final Object mCookieLock = new Object(); 62 private final Object mPasswordLock = new Object(); 63 private final Object mFormLock = new Object(); 64 private final Object mHttpAuthLock = new Object(); 65 66 private static final String mTableNames[] = { 67 "cookies", "password", "formurl", "formdata", "httpauth" 68 }; 69 70 // Table ids (they are index to mTableNames) 71 private static final int TABLE_COOKIES_ID = 0; 72 73 private static final int TABLE_PASSWORD_ID = 1; 74 75 private static final int TABLE_FORMURL_ID = 2; 76 77 private static final int TABLE_FORMDATA_ID = 3; 78 79 private static final int TABLE_HTTPAUTH_ID = 4; 80 81 // column id strings for "_id" which can be used by any table 82 private static final String ID_COL = "_id"; 83 84 private static final String[] ID_PROJECTION = new String[] { 85 "_id" 86 }; 87 88 // column id strings for "cookies" table 89 private static final String COOKIES_NAME_COL = "name"; 90 91 private static final String COOKIES_VALUE_COL = "value"; 92 93 private static final String COOKIES_DOMAIN_COL = "domain"; 94 95 private static final String COOKIES_PATH_COL = "path"; 96 97 private static final String COOKIES_EXPIRES_COL = "expires"; 98 99 private static final String COOKIES_SECURE_COL = "secure"; 100 101 // column id strings for "cache" table 102 private static final String CACHE_URL_COL = "url"; 103 104 private static final String CACHE_FILE_PATH_COL = "filepath"; 105 106 private static final String CACHE_LAST_MODIFY_COL = "lastmodify"; 107 108 private static final String CACHE_ETAG_COL = "etag"; 109 110 private static final String CACHE_EXPIRES_COL = "expires"; 111 112 private static final String CACHE_EXPIRES_STRING_COL = "expiresstring"; 113 114 private static final String CACHE_MIMETYPE_COL = "mimetype"; 115 116 private static final String CACHE_ENCODING_COL = "encoding"; 117 118 private static final String CACHE_HTTP_STATUS_COL = "httpstatus"; 119 120 private static final String CACHE_LOCATION_COL = "location"; 121 122 private static final String CACHE_CONTENTLENGTH_COL = "contentlength"; 123 124 private static final String CACHE_CONTENTDISPOSITION_COL = "contentdisposition"; 125 126 // column id strings for "password" table 127 private static final String PASSWORD_HOST_COL = "host"; 128 129 private static final String PASSWORD_USERNAME_COL = "username"; 130 131 private static final String PASSWORD_PASSWORD_COL = "password"; 132 133 // column id strings for "formurl" table 134 private static final String FORMURL_URL_COL = "url"; 135 136 // column id strings for "formdata" table 137 private static final String FORMDATA_URLID_COL = "urlid"; 138 139 private static final String FORMDATA_NAME_COL = "name"; 140 141 private static final String FORMDATA_VALUE_COL = "value"; 142 143 // column id strings for "httpauth" table 144 private static final String HTTPAUTH_HOST_COL = "host"; 145 146 private static final String HTTPAUTH_REALM_COL = "realm"; 147 148 private static final String HTTPAUTH_USERNAME_COL = "username"; 149 150 private static final String HTTPAUTH_PASSWORD_COL = "password"; 151 152 // use InsertHelper to improve insert performance by 40% 153 private static DatabaseUtils.InsertHelper mCacheInserter; 154 private static int mCacheUrlColIndex; 155 private static int mCacheFilePathColIndex; 156 private static int mCacheLastModifyColIndex; 157 private static int mCacheETagColIndex; 158 private static int mCacheExpiresColIndex; 159 private static int mCacheExpiresStringColIndex; 160 private static int mCacheMimeTypeColIndex; 161 private static int mCacheEncodingColIndex; 162 private static int mCacheHttpStatusColIndex; 163 private static int mCacheLocationColIndex; 164 private static int mCacheContentLengthColIndex; 165 private static int mCacheContentDispositionColIndex; 166 167 private static int mCacheTransactionRefcount; 168 169 private WebViewDatabase() { 170 // Singleton only, use getInstance() 171 } 172 173 public static synchronized WebViewDatabase getInstance(Context context) { 174 if (mInstance == null) { 175 mInstance = new WebViewDatabase(); 176 mDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0, null); 177 178 // mDatabase should not be null, 179 // the only case is RequestAPI test has problem to create db 180 if (mDatabase != null && mDatabase.getVersion() != DATABASE_VERSION) { 181 mDatabase.beginTransaction(); 182 try { 183 upgradeDatabase(); 184 mDatabase.setTransactionSuccessful(); 185 } finally { 186 mDatabase.endTransaction(); 187 } 188 } 189 190 if (mDatabase != null) { 191 // use per table Mutex lock, turn off database lock, this 192 // improves performance as database's ReentrantLock is expansive 193 mDatabase.setLockingEnabled(false); 194 } 195 196 mCacheDatabase = context.openOrCreateDatabase(CACHE_DATABASE_FILE, 197 0, null); 198 199 // mCacheDatabase should not be null, 200 // the only case is RequestAPI test has problem to create db 201 if (mCacheDatabase != null 202 && mCacheDatabase.getVersion() != CACHE_DATABASE_VERSION) { 203 mCacheDatabase.beginTransaction(); 204 try { 205 upgradeCacheDatabase(); 206 bootstrapCacheDatabase(); 207 mCacheDatabase.setTransactionSuccessful(); 208 } finally { 209 mCacheDatabase.endTransaction(); 210 } 211 // Erase the files from the file system in the 212 // case that the database was updated and the 213 // there were existing cache content 214 CacheManager.removeAllCacheFiles(); 215 } 216 217 if (mCacheDatabase != null) { 218 // use InsertHelper for faster insertion 219 mCacheInserter = new DatabaseUtils.InsertHelper(mCacheDatabase, 220 "cache"); 221 mCacheUrlColIndex = mCacheInserter 222 .getColumnIndex(CACHE_URL_COL); 223 mCacheFilePathColIndex = mCacheInserter 224 .getColumnIndex(CACHE_FILE_PATH_COL); 225 mCacheLastModifyColIndex = mCacheInserter 226 .getColumnIndex(CACHE_LAST_MODIFY_COL); 227 mCacheETagColIndex = mCacheInserter 228 .getColumnIndex(CACHE_ETAG_COL); 229 mCacheExpiresColIndex = mCacheInserter 230 .getColumnIndex(CACHE_EXPIRES_COL); 231 mCacheExpiresStringColIndex = mCacheInserter 232 .getColumnIndex(CACHE_EXPIRES_STRING_COL); 233 mCacheMimeTypeColIndex = mCacheInserter 234 .getColumnIndex(CACHE_MIMETYPE_COL); 235 mCacheEncodingColIndex = mCacheInserter 236 .getColumnIndex(CACHE_ENCODING_COL); 237 mCacheHttpStatusColIndex = mCacheInserter 238 .getColumnIndex(CACHE_HTTP_STATUS_COL); 239 mCacheLocationColIndex = mCacheInserter 240 .getColumnIndex(CACHE_LOCATION_COL); 241 mCacheContentLengthColIndex = mCacheInserter 242 .getColumnIndex(CACHE_CONTENTLENGTH_COL); 243 mCacheContentDispositionColIndex = mCacheInserter 244 .getColumnIndex(CACHE_CONTENTDISPOSITION_COL); 245 } 246 } 247 248 return mInstance; 249 } 250 251 private static void upgradeDatabase() { 252 int oldVersion = mDatabase.getVersion(); 253 if (oldVersion != 0) { 254 Log.i(LOGTAG, "Upgrading database from version " 255 + oldVersion + " to " 256 + DATABASE_VERSION + ", which will destroy old data"); 257 } 258 boolean justPasswords = 8 == oldVersion && 9 == DATABASE_VERSION; 259 if (!justPasswords) { 260 mDatabase.execSQL("DROP TABLE IF EXISTS " 261 + mTableNames[TABLE_COOKIES_ID]); 262 mDatabase.execSQL("DROP TABLE IF EXISTS cache"); 263 mDatabase.execSQL("DROP TABLE IF EXISTS " 264 + mTableNames[TABLE_FORMURL_ID]); 265 mDatabase.execSQL("DROP TABLE IF EXISTS " 266 + mTableNames[TABLE_FORMDATA_ID]); 267 mDatabase.execSQL("DROP TABLE IF EXISTS " 268 + mTableNames[TABLE_HTTPAUTH_ID]); 269 } 270 mDatabase.execSQL("DROP TABLE IF EXISTS " 271 + mTableNames[TABLE_PASSWORD_ID]); 272 273 mDatabase.setVersion(DATABASE_VERSION); 274 275 if (!justPasswords) { 276 // cookies 277 mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_COOKIES_ID] 278 + " (" + ID_COL + " INTEGER PRIMARY KEY, " 279 + COOKIES_NAME_COL + " TEXT, " + COOKIES_VALUE_COL 280 + " TEXT, " + COOKIES_DOMAIN_COL + " TEXT, " 281 + COOKIES_PATH_COL + " TEXT, " + COOKIES_EXPIRES_COL 282 + " INTEGER, " + COOKIES_SECURE_COL + " INTEGER" + ");"); 283 mDatabase.execSQL("CREATE INDEX cookiesIndex ON " 284 + mTableNames[TABLE_COOKIES_ID] + " (path)"); 285 286 // formurl 287 mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMURL_ID] 288 + " (" + ID_COL + " INTEGER PRIMARY KEY, " + FORMURL_URL_COL 289 + " TEXT" + ");"); 290 291 // formdata 292 mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMDATA_ID] 293 + " (" + ID_COL + " INTEGER PRIMARY KEY, " 294 + FORMDATA_URLID_COL + " INTEGER, " + FORMDATA_NAME_COL 295 + " TEXT, " + FORMDATA_VALUE_COL + " TEXT," + " UNIQUE (" 296 + FORMDATA_URLID_COL + ", " + FORMDATA_NAME_COL + ", " 297 + FORMDATA_VALUE_COL + ") ON CONFLICT IGNORE);"); 298 299 // httpauth 300 mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID] 301 + " (" + ID_COL + " INTEGER PRIMARY KEY, " 302 + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL 303 + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, " 304 + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE (" 305 + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL + ", " 306 + HTTPAUTH_USERNAME_COL + ") ON CONFLICT REPLACE);"); 307 } 308 // passwords 309 mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_PASSWORD_ID] 310 + " (" + ID_COL + " INTEGER PRIMARY KEY, " 311 + PASSWORD_HOST_COL + " TEXT, " + PASSWORD_USERNAME_COL 312 + " TEXT, " + PASSWORD_PASSWORD_COL + " TEXT," + " UNIQUE (" 313 + PASSWORD_HOST_COL + ", " + PASSWORD_USERNAME_COL 314 + ") ON CONFLICT REPLACE);"); 315 } 316 317 private static void upgradeCacheDatabase() { 318 int oldVersion = mCacheDatabase.getVersion(); 319 if (oldVersion != 0) { 320 Log.i(LOGTAG, "Upgrading cache database from version " 321 + oldVersion + " to " 322 + DATABASE_VERSION + ", which will destroy all old data"); 323 } 324 mCacheDatabase.execSQL("DROP TABLE IF EXISTS cache"); 325 mCacheDatabase.setVersion(CACHE_DATABASE_VERSION); 326 } 327 328 private static void bootstrapCacheDatabase() { 329 if (mCacheDatabase != null) { 330 mCacheDatabase.execSQL("CREATE TABLE cache" 331 + " (" + ID_COL + " INTEGER PRIMARY KEY, " + CACHE_URL_COL 332 + " TEXT, " + CACHE_FILE_PATH_COL + " TEXT, " 333 + CACHE_LAST_MODIFY_COL + " TEXT, " + CACHE_ETAG_COL 334 + " TEXT, " + CACHE_EXPIRES_COL + " INTEGER, " 335 + CACHE_EXPIRES_STRING_COL + " TEXT, " 336 + CACHE_MIMETYPE_COL + " TEXT, " + CACHE_ENCODING_COL 337 + " TEXT," + CACHE_HTTP_STATUS_COL + " INTEGER, " 338 + CACHE_LOCATION_COL + " TEXT, " + CACHE_CONTENTLENGTH_COL 339 + " INTEGER, " + CACHE_CONTENTDISPOSITION_COL + " TEXT, " 340 + " UNIQUE (" + CACHE_URL_COL + ") ON CONFLICT REPLACE);"); 341 mCacheDatabase.execSQL("CREATE INDEX cacheUrlIndex ON cache (" 342 + CACHE_URL_COL + ")"); 343 } 344 } 345 346 private boolean hasEntries(int tableId) { 347 if (mDatabase == null) { 348 return false; 349 } 350 351 Cursor cursor = mDatabase.query(mTableNames[tableId], ID_PROJECTION, 352 null, null, null, null, null); 353 boolean ret = cursor.moveToFirst() == true; 354 cursor.close(); 355 return ret; 356 } 357 358 // 359 // cookies functions 360 // 361 362 /** 363 * Get cookies in the format of CookieManager.Cookie inside an ArrayList for 364 * a given domain 365 * 366 * @return ArrayList<Cookie> If nothing is found, return an empty list. 367 */ 368 ArrayList<Cookie> getCookiesForDomain(String domain) { 369 ArrayList<Cookie> list = new ArrayList<Cookie>(); 370 if (domain == null || mDatabase == null) { 371 return list; 372 } 373 374 synchronized (mCookieLock) { 375 final String[] columns = new String[] { 376 ID_COL, COOKIES_DOMAIN_COL, COOKIES_PATH_COL, 377 COOKIES_NAME_COL, COOKIES_VALUE_COL, COOKIES_EXPIRES_COL, 378 COOKIES_SECURE_COL 379 }; 380 final String selection = "(" + COOKIES_DOMAIN_COL 381 + " GLOB '*' || ?)"; 382 Cursor cursor = mDatabase.query(mTableNames[TABLE_COOKIES_ID], 383 columns, selection, new String[] { domain }, null, null, 384 null); 385 if (cursor.moveToFirst()) { 386 int domainCol = cursor.getColumnIndex(COOKIES_DOMAIN_COL); 387 int pathCol = cursor.getColumnIndex(COOKIES_PATH_COL); 388 int nameCol = cursor.getColumnIndex(COOKIES_NAME_COL); 389 int valueCol = cursor.getColumnIndex(COOKIES_VALUE_COL); 390 int expiresCol = cursor.getColumnIndex(COOKIES_EXPIRES_COL); 391 int secureCol = cursor.getColumnIndex(COOKIES_SECURE_COL); 392 do { 393 Cookie cookie = new Cookie(); 394 cookie.domain = cursor.getString(domainCol); 395 cookie.path = cursor.getString(pathCol); 396 cookie.name = cursor.getString(nameCol); 397 cookie.value = cursor.getString(valueCol); 398 if (cursor.isNull(expiresCol)) { 399 cookie.expires = -1; 400 } else { 401 cookie.expires = cursor.getLong(expiresCol); 402 } 403 cookie.secure = cursor.getShort(secureCol) != 0; 404 cookie.mode = Cookie.MODE_NORMAL; 405 list.add(cookie); 406 } while (cursor.moveToNext()); 407 } 408 cursor.close(); 409 return list; 410 } 411 } 412 413 /** 414 * Delete cookies which matches (domain, path, name). 415 * 416 * @param domain If it is null, nothing happens. 417 * @param path If it is null, all the cookies match (domain) will be 418 * deleted. 419 * @param name If it is null, all the cookies match (domain, path) will be 420 * deleted. 421 */ 422 void deleteCookies(String domain, String path, String name) { 423 if (domain == null || mDatabase == null) { 424 return; 425 } 426 427 synchronized (mCookieLock) { 428 final String where = "(" + COOKIES_DOMAIN_COL + " == ?) AND (" 429 + COOKIES_PATH_COL + " == ?) AND (" + COOKIES_NAME_COL 430 + " == ?)"; 431 mDatabase.delete(mTableNames[TABLE_COOKIES_ID], where, 432 new String[] { domain, path, name }); 433 } 434 } 435 436 /** 437 * Add a cookie to the database 438 * 439 * @param cookie 440 */ 441 void addCookie(Cookie cookie) { 442 if (cookie.domain == null || cookie.path == null || cookie.name == null 443 || mDatabase == null) { 444 return; 445 } 446 447 synchronized (mCookieLock) { 448 ContentValues cookieVal = new ContentValues(); 449 cookieVal.put(COOKIES_DOMAIN_COL, cookie.domain); 450 cookieVal.put(COOKIES_PATH_COL, cookie.path); 451 cookieVal.put(COOKIES_NAME_COL, cookie.name); 452 cookieVal.put(COOKIES_VALUE_COL, cookie.value); 453 if (cookie.expires != -1) { 454 cookieVal.put(COOKIES_EXPIRES_COL, cookie.expires); 455 } 456 cookieVal.put(COOKIES_SECURE_COL, cookie.secure); 457 mDatabase.insert(mTableNames[TABLE_COOKIES_ID], null, cookieVal); 458 } 459 } 460 461 /** 462 * Whether there is any cookies in the database 463 * 464 * @return TRUE if there is cookie. 465 */ 466 boolean hasCookies() { 467 synchronized (mCookieLock) { 468 return hasEntries(TABLE_COOKIES_ID); 469 } 470 } 471 472 /** 473 * Clear cookie database 474 */ 475 void clearCookies() { 476 if (mDatabase == null) { 477 return; 478 } 479 480 synchronized (mCookieLock) { 481 mDatabase.delete(mTableNames[TABLE_COOKIES_ID], null, null); 482 } 483 } 484 485 /** 486 * Clear session cookies, which means cookie doesn't have EXPIRES. 487 */ 488 void clearSessionCookies() { 489 if (mDatabase == null) { 490 return; 491 } 492 493 final String sessionExpired = COOKIES_EXPIRES_COL + " ISNULL"; 494 synchronized (mCookieLock) { 495 mDatabase.delete(mTableNames[TABLE_COOKIES_ID], sessionExpired, 496 null); 497 } 498 } 499 500 /** 501 * Clear expired cookies 502 * 503 * @param now Time for now 504 */ 505 void clearExpiredCookies(long now) { 506 if (mDatabase == null) { 507 return; 508 } 509 510 final String expires = COOKIES_EXPIRES_COL + " <= ?"; 511 synchronized (mCookieLock) { 512 mDatabase.delete(mTableNames[TABLE_COOKIES_ID], expires, 513 new String[] { Long.toString(now) }); 514 } 515 } 516 517 // 518 // cache functions, can only be called from WebCoreThread 519 // 520 521 boolean startCacheTransaction() { 522 if (++mCacheTransactionRefcount == 1) { 523 mCacheDatabase.beginTransaction(); 524 return true; 525 } 526 return false; 527 } 528 529 boolean endCacheTransaction() { 530 if (--mCacheTransactionRefcount == 0) { 531 try { 532 mCacheDatabase.setTransactionSuccessful(); 533 } finally { 534 mCacheDatabase.endTransaction(); 535 } 536 return true; 537 } 538 return false; 539 } 540 541 /** 542 * Get a cache item. 543 * 544 * @param url The url 545 * @return CacheResult The CacheManager.CacheResult 546 */ 547 CacheResult getCache(String url) { 548 if (url == null || mCacheDatabase == null) { 549 return null; 550 } 551 552 Cursor cursor = mCacheDatabase.rawQuery("SELECT filepath, lastmodify, etag, expires, " 553 + "expiresstring, mimetype, encoding, httpstatus, location, contentlength, " 554 + "contentdisposition FROM cache WHERE url = ?", 555 new String[] { url }); 556 557 try { 558 if (cursor.moveToFirst()) { 559 CacheResult ret = new CacheResult(); 560 ret.localPath = cursor.getString(0); 561 ret.lastModified = cursor.getString(1); 562 ret.etag = cursor.getString(2); 563 ret.expires = cursor.getLong(3); 564 ret.expiresString = cursor.getString(4); 565 ret.mimeType = cursor.getString(5); 566 ret.encoding = cursor.getString(6); 567 ret.httpStatusCode = cursor.getInt(7); 568 ret.location = cursor.getString(8); 569 ret.contentLength = cursor.getLong(9); 570 ret.contentdisposition = cursor.getString(10); 571 return ret; 572 } 573 } finally { 574 if (cursor != null) cursor.close(); 575 } 576 return null; 577 } 578 579 /** 580 * Remove a cache item. 581 * 582 * @param url The url 583 */ 584 void removeCache(String url) { 585 if (url == null || mCacheDatabase == null) { 586 return; 587 } 588 589 mCacheDatabase.execSQL("DELETE FROM cache WHERE url = ?", new String[] { url }); 590 } 591 592 /** 593 * Add or update a cache. CACHE_URL_COL is unique in the table. 594 * 595 * @param url The url 596 * @param c The CacheManager.CacheResult 597 */ 598 void addCache(String url, CacheResult c) { 599 if (url == null || mCacheDatabase == null) { 600 return; 601 } 602 603 mCacheInserter.prepareForInsert(); 604 mCacheInserter.bind(mCacheUrlColIndex, url); 605 mCacheInserter.bind(mCacheFilePathColIndex, c.localPath); 606 mCacheInserter.bind(mCacheLastModifyColIndex, c.lastModified); 607 mCacheInserter.bind(mCacheETagColIndex, c.etag); 608 mCacheInserter.bind(mCacheExpiresColIndex, c.expires); 609 mCacheInserter.bind(mCacheExpiresStringColIndex, c.expiresString); 610 mCacheInserter.bind(mCacheMimeTypeColIndex, c.mimeType); 611 mCacheInserter.bind(mCacheEncodingColIndex, c.encoding); 612 mCacheInserter.bind(mCacheHttpStatusColIndex, c.httpStatusCode); 613 mCacheInserter.bind(mCacheLocationColIndex, c.location); 614 mCacheInserter.bind(mCacheContentLengthColIndex, c.contentLength); 615 mCacheInserter.bind(mCacheContentDispositionColIndex, 616 c.contentdisposition); 617 mCacheInserter.execute(); 618 } 619 620 /** 621 * Clear cache database 622 */ 623 void clearCache() { 624 if (mCacheDatabase == null) { 625 return; 626 } 627 628 mCacheDatabase.delete("cache", null, null); 629 } 630 631 boolean hasCache() { 632 if (mCacheDatabase == null) { 633 return false; 634 } 635 636 Cursor cursor = mCacheDatabase.query("cache", ID_PROJECTION, 637 null, null, null, null, null); 638 boolean ret = cursor.moveToFirst() == true; 639 cursor.close(); 640 return ret; 641 } 642 643 long getCacheTotalSize() { 644 long size = 0; 645 Cursor cursor = mCacheDatabase.rawQuery( 646 "SELECT SUM(contentlength) as sum FROM cache", null); 647 if (cursor.moveToFirst()) { 648 size = cursor.getLong(0); 649 } 650 cursor.close(); 651 return size; 652 } 653 654 ArrayList<String> trimCache(long amount) { 655 ArrayList<String> pathList = new ArrayList<String>(100); 656 Cursor cursor = mCacheDatabase.rawQuery( 657 "SELECT contentlength, filepath FROM cache ORDER BY expires ASC", 658 null); 659 if (cursor.moveToFirst()) { 660 int batchSize = 100; 661 StringBuilder pathStr = new StringBuilder(20 + 16 * batchSize); 662 pathStr.append("DELETE FROM cache WHERE filepath IN (?"); 663 for (int i = 1; i < batchSize; i++) { 664 pathStr.append(", ?"); 665 } 666 pathStr.append(")"); 667 SQLiteStatement statement = mCacheDatabase.compileStatement(pathStr 668 .toString()); 669 // as bindString() uses 1-based index, initialize index to 1 670 int index = 1; 671 do { 672 long length = cursor.getLong(0); 673 if (length == 0) { 674 continue; 675 } 676 amount -= length; 677 String filePath = cursor.getString(1); 678 statement.bindString(index, filePath); 679 pathList.add(filePath); 680 if (index++ == batchSize) { 681 statement.execute(); 682 statement.clearBindings(); 683 index = 1; 684 } 685 } while (cursor.moveToNext() && amount > 0); 686 if (index > 1) { 687 // there may be old bindings from the previous statement if 688 // index is less than batchSize, which is Ok. 689 statement.execute(); 690 } 691 statement.close(); 692 } 693 cursor.close(); 694 return pathList; 695 } 696 697 // 698 // password functions 699 // 700 701 /** 702 * Set password. Tuple (PASSWORD_HOST_COL, PASSWORD_USERNAME_COL) is unique. 703 * 704 * @param schemePlusHost The scheme and host for the password 705 * @param username The username for the password. If it is null, it means 706 * password can't be saved. 707 * @param password The password 708 */ 709 void setUsernamePassword(String schemePlusHost, String username, 710 String password) { 711 if (schemePlusHost == null || mDatabase == null) { 712 return; 713 } 714 715 synchronized (mPasswordLock) { 716 final ContentValues c = new ContentValues(); 717 c.put(PASSWORD_HOST_COL, schemePlusHost); 718 c.put(PASSWORD_USERNAME_COL, username); 719 c.put(PASSWORD_PASSWORD_COL, password); 720 mDatabase.insert(mTableNames[TABLE_PASSWORD_ID], PASSWORD_HOST_COL, 721 c); 722 } 723 } 724 725 /** 726 * Retrieve the username and password for a given host 727 * 728 * @param schemePlusHost The scheme and host which passwords applies to 729 * @return String[] if found, String[0] is username, which can be null and 730 * String[1] is password. Return null if it can't find anything. 731 */ 732 String[] getUsernamePassword(String schemePlusHost) { 733 if (schemePlusHost == null || mDatabase == null) { 734 return null; 735 } 736 737 final String[] columns = new String[] { 738 PASSWORD_USERNAME_COL, PASSWORD_PASSWORD_COL 739 }; 740 final String selection = "(" + PASSWORD_HOST_COL + " == ?)"; 741 synchronized (mPasswordLock) { 742 String[] ret = null; 743 Cursor cursor = mDatabase.query(mTableNames[TABLE_PASSWORD_ID], 744 columns, selection, new String[] { schemePlusHost }, null, 745 null, null); 746 if (cursor.moveToFirst()) { 747 ret = new String[2]; 748 ret[0] = cursor.getString( 749 cursor.getColumnIndex(PASSWORD_USERNAME_COL)); 750 ret[1] = cursor.getString( 751 cursor.getColumnIndex(PASSWORD_PASSWORD_COL)); 752 } 753 cursor.close(); 754 return ret; 755 } 756 } 757 758 /** 759 * Find out if there are any passwords saved. 760 * 761 * @return TRUE if there is passwords saved 762 */ 763 public boolean hasUsernamePassword() { 764 synchronized (mPasswordLock) { 765 return hasEntries(TABLE_PASSWORD_ID); 766 } 767 } 768 769 /** 770 * Clear password database 771 */ 772 public void clearUsernamePassword() { 773 if (mDatabase == null) { 774 return; 775 } 776 777 synchronized (mPasswordLock) { 778 mDatabase.delete(mTableNames[TABLE_PASSWORD_ID], null, null); 779 } 780 } 781 782 // 783 // http authentication password functions 784 // 785 786 /** 787 * Set HTTP authentication password. Tuple (HTTPAUTH_HOST_COL, 788 * HTTPAUTH_REALM_COL, HTTPAUTH_USERNAME_COL) is unique. 789 * 790 * @param host The host for the password 791 * @param realm The realm for the password 792 * @param username The username for the password. If it is null, it means 793 * password can't be saved. 794 * @param password The password 795 */ 796 void setHttpAuthUsernamePassword(String host, String realm, String username, 797 String password) { 798 if (host == null || realm == null || mDatabase == null) { 799 return; 800 } 801 802 synchronized (mHttpAuthLock) { 803 final ContentValues c = new ContentValues(); 804 c.put(HTTPAUTH_HOST_COL, host); 805 c.put(HTTPAUTH_REALM_COL, realm); 806 c.put(HTTPAUTH_USERNAME_COL, username); 807 c.put(HTTPAUTH_PASSWORD_COL, password); 808 mDatabase.insert(mTableNames[TABLE_HTTPAUTH_ID], HTTPAUTH_HOST_COL, 809 c); 810 } 811 } 812 813 /** 814 * Retrieve the HTTP authentication username and password for a given 815 * host+realm pair 816 * 817 * @param host The host the password applies to 818 * @param realm The realm the password applies to 819 * @return String[] if found, String[0] is username, which can be null and 820 * String[1] is password. Return null if it can't find anything. 821 */ 822 String[] getHttpAuthUsernamePassword(String host, String realm) { 823 if (host == null || realm == null || mDatabase == null){ 824 return null; 825 } 826 827 final String[] columns = new String[] { 828 HTTPAUTH_USERNAME_COL, HTTPAUTH_PASSWORD_COL 829 }; 830 final String selection = "(" + HTTPAUTH_HOST_COL + " == ?) AND (" 831 + HTTPAUTH_REALM_COL + " == ?)"; 832 synchronized (mHttpAuthLock) { 833 String[] ret = null; 834 Cursor cursor = mDatabase.query(mTableNames[TABLE_HTTPAUTH_ID], 835 columns, selection, new String[] { host, realm }, null, 836 null, null); 837 if (cursor.moveToFirst()) { 838 ret = new String[2]; 839 ret[0] = cursor.getString( 840 cursor.getColumnIndex(HTTPAUTH_USERNAME_COL)); 841 ret[1] = cursor.getString( 842 cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL)); 843 } 844 cursor.close(); 845 return ret; 846 } 847 } 848 849 /** 850 * Find out if there are any HTTP authentication passwords saved. 851 * 852 * @return TRUE if there are passwords saved 853 */ 854 public boolean hasHttpAuthUsernamePassword() { 855 synchronized (mHttpAuthLock) { 856 return hasEntries(TABLE_HTTPAUTH_ID); 857 } 858 } 859 860 /** 861 * Clear HTTP authentication password database 862 */ 863 public void clearHttpAuthUsernamePassword() { 864 if (mDatabase == null) { 865 return; 866 } 867 868 synchronized (mHttpAuthLock) { 869 mDatabase.delete(mTableNames[TABLE_HTTPAUTH_ID], null, null); 870 } 871 } 872 873 // 874 // form data functions 875 // 876 877 /** 878 * Set form data for a site. Tuple (FORMDATA_URLID_COL, FORMDATA_NAME_COL, 879 * FORMDATA_VALUE_COL) is unique 880 * 881 * @param url The url of the site 882 * @param formdata The form data in HashMap 883 */ 884 void setFormData(String url, HashMap<String, String> formdata) { 885 if (url == null || formdata == null || mDatabase == null) { 886 return; 887 } 888 889 final String selection = "(" + FORMURL_URL_COL + " == ?)"; 890 synchronized (mFormLock) { 891 long urlid = -1; 892 Cursor cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID], 893 ID_PROJECTION, selection, new String[] { url }, null, null, 894 null); 895 if (cursor.moveToFirst()) { 896 urlid = cursor.getLong(cursor.getColumnIndex(ID_COL)); 897 } else { 898 ContentValues c = new ContentValues(); 899 c.put(FORMURL_URL_COL, url); 900 urlid = mDatabase.insert( 901 mTableNames[TABLE_FORMURL_ID], null, c); 902 } 903 cursor.close(); 904 if (urlid >= 0) { 905 Set<Entry<String, String>> set = formdata.entrySet(); 906 Iterator<Entry<String, String>> iter = set.iterator(); 907 ContentValues map = new ContentValues(); 908 map.put(FORMDATA_URLID_COL, urlid); 909 while (iter.hasNext()) { 910 Entry<String, String> entry = iter.next(); 911 map.put(FORMDATA_NAME_COL, entry.getKey()); 912 map.put(FORMDATA_VALUE_COL, entry.getValue()); 913 mDatabase.insert(mTableNames[TABLE_FORMDATA_ID], null, map); 914 } 915 } 916 } 917 } 918 919 /** 920 * Get all the values for a form entry with "name" in a given site 921 * 922 * @param url The url of the site 923 * @param name The name of the form entry 924 * @return A list of values. Return empty list if nothing is found. 925 */ 926 ArrayList<String> getFormData(String url, String name) { 927 ArrayList<String> values = new ArrayList<String>(); 928 if (url == null || name == null || mDatabase == null) { 929 return values; 930 } 931 932 final String urlSelection = "(" + FORMURL_URL_COL + " == ?)"; 933 final String dataSelection = "(" + FORMDATA_URLID_COL + " == ?) AND (" 934 + FORMDATA_NAME_COL + " == ?)"; 935 synchronized (mFormLock) { 936 Cursor cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID], 937 ID_PROJECTION, urlSelection, new String[] { url }, null, 938 null, null); 939 if (cursor.moveToFirst()) { 940 long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL)); 941 Cursor dataCursor = mDatabase.query( 942 mTableNames[TABLE_FORMDATA_ID], 943 new String[] { ID_COL, FORMDATA_VALUE_COL }, 944 dataSelection, 945 new String[] { Long.toString(urlid), name }, null, 946 null, null); 947 if (dataCursor.moveToFirst()) { 948 int valueCol = 949 dataCursor.getColumnIndex(FORMDATA_VALUE_COL); 950 do { 951 values.add(dataCursor.getString(valueCol)); 952 } while (dataCursor.moveToNext()); 953 } 954 dataCursor.close(); 955 } 956 cursor.close(); 957 return values; 958 } 959 } 960 961 /** 962 * Find out if there is form data saved. 963 * 964 * @return TRUE if there is form data in the database 965 */ 966 public boolean hasFormData() { 967 synchronized (mFormLock) { 968 return hasEntries(TABLE_FORMURL_ID); 969 } 970 } 971 972 /** 973 * Clear form database 974 */ 975 public void clearFormData() { 976 if (mDatabase == null) { 977 return; 978 } 979 980 synchronized (mFormLock) { 981 mDatabase.delete(mTableNames[TABLE_FORMURL_ID], null, null); 982 mDatabase.delete(mTableNames[TABLE_FORMDATA_ID], null, null); 983 } 984 } 985} 986