1524 lines
58 KiB
Java
1524 lines
58 KiB
Java
|
/*
|
||
|
* Copyright (C) 2007 The Android Open Source Project
|
||
|
* Copyright (C) 2011, The Linux Foundation. All rights reserved.
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
package android.webkit;
|
||
|
|
||
|
import java.util.ArrayList;
|
||
|
import java.util.HashMap;
|
||
|
import java.util.Iterator;
|
||
|
import java.util.List;
|
||
|
import java.util.Map;
|
||
|
import java.util.Set;
|
||
|
import java.util.HashSet;
|
||
|
import java.util.HashMap;
|
||
|
import java.util.LinkedList;
|
||
|
import java.util.ListIterator;
|
||
|
import java.util.Map.Entry;
|
||
|
|
||
|
import android.content.ContentValues;
|
||
|
import android.content.Context;
|
||
|
import android.database.Cursor;
|
||
|
import android.database.DatabaseUtils;
|
||
|
import android.database.sqlite.SQLiteDatabase;
|
||
|
import android.database.sqlite.SQLiteException;
|
||
|
import android.database.sqlite.SQLiteStatement;
|
||
|
import android.os.SystemProperties;
|
||
|
import android.util.Log;
|
||
|
import android.webkit.CookieManager.Cookie;
|
||
|
import android.webkit.CacheManager.CacheResult;
|
||
|
|
||
|
public class WebViewDatabase {
|
||
|
private static final String DATABASE_FILE = "webview.db";
|
||
|
private static final String CACHE_DATABASE_FILE = "webviewCache.db";
|
||
|
|
||
|
// log tag
|
||
|
protected static final String LOGTAG = "webviewdatabase";
|
||
|
|
||
|
private static final int DATABASE_VERSION = 11;
|
||
|
// 2 -> 3 Modified Cache table to allow cache of redirects
|
||
|
// 3 -> 4 Added Oma-Downloads table
|
||
|
// 4 -> 5 Modified Cache table to support persistent contentLength
|
||
|
// 5 -> 4 Removed Oma-Downoads table
|
||
|
// 5 -> 6 Add INDEX for cache table
|
||
|
// 6 -> 7 Change cache localPath from int to String
|
||
|
// 7 -> 8 Move cache to its own db
|
||
|
// 8 -> 9 Store both scheme and host when storing passwords
|
||
|
// 9 -> 10 Update httpauth table UNIQUE
|
||
|
// 10 -> 11 Added subhosts history table for tcp pre connection
|
||
|
private static final int CACHE_DATABASE_VERSION = 5;
|
||
|
// 1 -> 2 Add expires String
|
||
|
// 2 -> 3 Add content-disposition
|
||
|
// 3 -> 4 Add crossdomain (For x-permitted-cross-domain-policies header)
|
||
|
// 4 -> 5 Add last-acces-time, access-counter, weight
|
||
|
|
||
|
private static WebViewDatabase mInstance = null;
|
||
|
|
||
|
private static SQLiteDatabase mDatabase = null;
|
||
|
private static SQLiteDatabase mCacheDatabase = null;
|
||
|
|
||
|
// synchronize locks
|
||
|
private final Object mCookieLock = new Object();
|
||
|
private final Object mPasswordLock = new Object();
|
||
|
private final Object mFormLock = new Object();
|
||
|
private final Object mHttpAuthLock = new Object();
|
||
|
private final Object mTcpPreConnectionLock = new Object();
|
||
|
|
||
|
private static final String mTableNames[] = {
|
||
|
"cookies", "password", "formurl", "formdata", "httpauth", "tcppreconnection"
|
||
|
};
|
||
|
|
||
|
// Table ids (they are index to mTableNames)
|
||
|
private static final int TABLE_COOKIES_ID = 0;
|
||
|
|
||
|
private static final int TABLE_PASSWORD_ID = 1;
|
||
|
|
||
|
private static final int TABLE_FORMURL_ID = 2;
|
||
|
|
||
|
private static final int TABLE_FORMDATA_ID = 3;
|
||
|
|
||
|
private static final int TABLE_HTTPAUTH_ID = 4;
|
||
|
|
||
|
private static final int TABLE_TCPPRECONNECTION_ID = 5;
|
||
|
|
||
|
// column id strings for "_id" which can be used by any table
|
||
|
private static final String ID_COL = "_id";
|
||
|
|
||
|
private static final String[] ID_PROJECTION = new String[] {
|
||
|
"_id"
|
||
|
};
|
||
|
|
||
|
// column id strings for "cookies" table
|
||
|
private static final String COOKIES_NAME_COL = "name";
|
||
|
|
||
|
private static final String COOKIES_VALUE_COL = "value";
|
||
|
|
||
|
private static final String COOKIES_DOMAIN_COL = "domain";
|
||
|
|
||
|
private static final String COOKIES_PATH_COL = "path";
|
||
|
|
||
|
private static final String COOKIES_EXPIRES_COL = "expires";
|
||
|
|
||
|
private static final String COOKIES_SECURE_COL = "secure";
|
||
|
|
||
|
// column id strings for "cache" table
|
||
|
private static final String CACHE_URL_COL = "url";
|
||
|
|
||
|
private static final String CACHE_FILE_PATH_COL = "filepath";
|
||
|
|
||
|
private static final String CACHE_LAST_MODIFY_COL = "lastmodify";
|
||
|
|
||
|
private static final String CACHE_ETAG_COL = "etag";
|
||
|
|
||
|
private static final String CACHE_EXPIRES_COL = "expires";
|
||
|
|
||
|
private static final String CACHE_EXPIRES_STRING_COL = "expiresstring";
|
||
|
|
||
|
private static final String CACHE_MIMETYPE_COL = "mimetype";
|
||
|
|
||
|
private static final String CACHE_ENCODING_COL = "encoding";
|
||
|
|
||
|
private static final String CACHE_HTTP_STATUS_COL = "httpstatus";
|
||
|
|
||
|
private static final String CACHE_LOCATION_COL = "location";
|
||
|
|
||
|
private static final String CACHE_CONTENTLENGTH_COL = "contentlength";
|
||
|
|
||
|
private static final String CACHE_CONTENTDISPOSITION_COL = "contentdisposition";
|
||
|
|
||
|
private static final String CACHE_CROSSDOMAIN_COL = "crossdomain";
|
||
|
|
||
|
private static final String CACHE_LASTACCESSTIME_COL = "lastaccesstime";
|
||
|
|
||
|
private static final String CACHE_ACCESSCOUNTER_COL = "accesscounter";
|
||
|
|
||
|
private static final String CACHE_WEIGHT_COL = "weight";
|
||
|
|
||
|
// column id strings for "password" table
|
||
|
private static final String PASSWORD_HOST_COL = "host";
|
||
|
|
||
|
private static final String PASSWORD_USERNAME_COL = "username";
|
||
|
|
||
|
private static final String PASSWORD_PASSWORD_COL = "password";
|
||
|
|
||
|
// column id strings for "formurl" table
|
||
|
private static final String FORMURL_URL_COL = "url";
|
||
|
|
||
|
// column id strings for "formdata" table
|
||
|
private static final String FORMDATA_URLID_COL = "urlid";
|
||
|
|
||
|
private static final String FORMDATA_NAME_COL = "name";
|
||
|
|
||
|
private static final String FORMDATA_VALUE_COL = "value";
|
||
|
|
||
|
// column id strings for "httpauth" table
|
||
|
private static final String HTTPAUTH_HOST_COL = "host";
|
||
|
|
||
|
private static final String HTTPAUTH_REALM_COL = "realm";
|
||
|
|
||
|
private static final String HTTPAUTH_USERNAME_COL = "username";
|
||
|
|
||
|
private static final String HTTPAUTH_PASSWORD_COL = "password";
|
||
|
|
||
|
// column id strings for "tcppreconnection" table
|
||
|
private static final String TCPPRECONNECTION_MAIN_URL_COL = "mainurl";
|
||
|
|
||
|
private static final String TCPPRECONNECTION_MAIN_URL_USECOUNT_COL = "mainurlusecount";
|
||
|
|
||
|
private static final String TCPPRECONNECTION_SUBHOST_COL = "subhost";
|
||
|
|
||
|
private static final String TCPPRECONNECTION_SUBHOST_USECOUNT_COL = "subhostusecount";
|
||
|
|
||
|
private static final String TCPPRECONNECTION_SUBHOST_WEIGHT_COL = "subhostweight";
|
||
|
|
||
|
// use InsertHelper to improve insert performance by 40%
|
||
|
private static DatabaseUtils.InsertHelper mCacheInserter;
|
||
|
private static int mCacheUrlColIndex;
|
||
|
private static int mCacheFilePathColIndex;
|
||
|
private static int mCacheLastModifyColIndex;
|
||
|
private static int mCacheETagColIndex;
|
||
|
private static int mCacheExpiresColIndex;
|
||
|
private static int mCacheExpiresStringColIndex;
|
||
|
private static int mCacheMimeTypeColIndex;
|
||
|
private static int mCacheEncodingColIndex;
|
||
|
private static int mCacheHttpStatusColIndex;
|
||
|
private static int mCacheLocationColIndex;
|
||
|
private static int mCacheContentLengthColIndex;
|
||
|
private static int mCacheContentDispositionColIndex;
|
||
|
private static int mCacheCrossDomainColIndex;
|
||
|
private static int mCacheLastAccessTimeIndex;
|
||
|
private static int mCacheAccessCounterIndex;
|
||
|
private static int mCacheWeightIndex;
|
||
|
|
||
|
private static int mCacheTransactionRefcount;
|
||
|
|
||
|
private static final int CACHE_STAT_ID_OTHER = 0;
|
||
|
private static final int CACHE_STAT_ID_HTML = 1;
|
||
|
private static final int CACHE_STAT_ID_CSS = 2;
|
||
|
private static final int CACHE_STAT_ID_JS = 3;
|
||
|
private static final int CACHE_STAT_ID_IMAGE = 4;
|
||
|
|
||
|
private static final String CACHE_ORDER_BY_DEF = CACHE_EXPIRES_COL;
|
||
|
private static String CACHE_ORDER_BY = CACHE_ORDER_BY_DEF;
|
||
|
|
||
|
private static final int CACHE_EVICT_EXPIRED_DEF = 0;
|
||
|
private static final long CACHE_PRIO_ADVANCE_STEP_DEF = 0;
|
||
|
private static final long CACHE_WEIGHT_ADVANCE_STEP_DEF = 0;
|
||
|
|
||
|
private static int CACHE_EVICT_EXPIRED = CACHE_EVICT_EXPIRED_DEF;
|
||
|
private static long CACHE_ADVANCE_STEP_PRIO = CACHE_PRIO_ADVANCE_STEP_DEF;
|
||
|
private static long CACHE_ADVANCE_STEP_WEIGHT = CACHE_WEIGHT_ADVANCE_STEP_DEF;
|
||
|
|
||
|
private static class CacheAccessStat {
|
||
|
long mLastAccessTime = 0;
|
||
|
int mCacheAccessCounter = 0;
|
||
|
int mCacheItemPriority = 0;
|
||
|
long mContentLength = 0;
|
||
|
long mWeight = 0;
|
||
|
|
||
|
public CacheAccessStat(CacheResult c) {
|
||
|
mCacheAccessCounter = c.accessCounter;
|
||
|
mCacheItemPriority = getCacheItemPriority(c.getMimeType());
|
||
|
mContentLength = c.getContentLength();
|
||
|
hit();
|
||
|
}
|
||
|
|
||
|
public void hit() {
|
||
|
mCacheAccessCounter++;
|
||
|
mLastAccessTime = System.currentTimeMillis();
|
||
|
mWeight = mLastAccessTime + CACHE_ADVANCE_STEP_PRIO * mCacheItemPriority;
|
||
|
mWeight += CACHE_ADVANCE_STEP_WEIGHT * normalize(mContentLength/mCacheAccessCounter);
|
||
|
}
|
||
|
|
||
|
int getCacheStatId(String mimeType) {
|
||
|
if (mimeType.contains("image")) {
|
||
|
return CACHE_STAT_ID_IMAGE;
|
||
|
}
|
||
|
if (mimeType.contains("javascript") || mimeType.contains("js")) {
|
||
|
return CACHE_STAT_ID_JS;
|
||
|
}
|
||
|
if (mimeType.contains("css")) {
|
||
|
return CACHE_STAT_ID_CSS;
|
||
|
}
|
||
|
if (mimeType.contains("html")) {
|
||
|
return CACHE_STAT_ID_HTML;
|
||
|
}
|
||
|
return CACHE_STAT_ID_OTHER;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get resource priority
|
||
|
*/
|
||
|
int getCacheItemPriority(String mimeType) {
|
||
|
int id = getCacheStatId(mimeType);
|
||
|
switch (id) {
|
||
|
case CACHE_STAT_ID_CSS:
|
||
|
case CACHE_STAT_ID_HTML:
|
||
|
return 2;
|
||
|
case CACHE_STAT_ID_JS:
|
||
|
return 1;
|
||
|
case CACHE_STAT_ID_IMAGE:
|
||
|
return 0;
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int normalize(long i) {
|
||
|
int normalized = 0;
|
||
|
|
||
|
if ((i & (i - 1)) != 0) {
|
||
|
normalized += 1;
|
||
|
}
|
||
|
for (int index = 16; index >= 2; index = index/2 ) {
|
||
|
if ((i >> index) != 0) {
|
||
|
normalized += index; i >>= index;
|
||
|
}
|
||
|
}
|
||
|
if ((i >> 1) != 0) {
|
||
|
normalized += 1;
|
||
|
}
|
||
|
return (32 - normalized);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private final Object mCacheStatLock = new Object();
|
||
|
private static HashMap<String, CacheAccessStat> mCacheStat = new HashMap<String, CacheAccessStat>();
|
||
|
|
||
|
private WebViewDatabase() {
|
||
|
// Singleton only, use getInstance()
|
||
|
}
|
||
|
|
||
|
public static synchronized WebViewDatabase getInstance(Context context) {
|
||
|
if (mInstance == null) {
|
||
|
mInstance = new WebViewDatabase();
|
||
|
|
||
|
CACHE_EVICT_EXPIRED = SystemProperties
|
||
|
.getInt("net.nw.cache.evictexpired", CACHE_EVICT_EXPIRED_DEF);
|
||
|
CACHE_ADVANCE_STEP_PRIO = SystemProperties
|
||
|
.getLong("net.nw.cache.prioadvstep", CACHE_PRIO_ADVANCE_STEP_DEF);
|
||
|
CACHE_ADVANCE_STEP_WEIGHT = SystemProperties
|
||
|
.getLong("net.nw.cache.weightadvstep", CACHE_WEIGHT_ADVANCE_STEP_DEF);
|
||
|
CACHE_ORDER_BY = SystemProperties
|
||
|
.get("net.nw.cache.orderby", CACHE_ORDER_BY_DEF);
|
||
|
|
||
|
try {
|
||
|
mDatabase = context
|
||
|
.openOrCreateDatabase(DATABASE_FILE, 0, null);
|
||
|
} catch (SQLiteException e) {
|
||
|
// try again by deleting the old db and create a new one
|
||
|
if (context.deleteDatabase(DATABASE_FILE)) {
|
||
|
mDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0,
|
||
|
null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// mDatabase should not be null,
|
||
|
// the only case is RequestAPI test has problem to create db
|
||
|
if (mDatabase != null && mDatabase.getVersion() != DATABASE_VERSION) {
|
||
|
mDatabase.beginTransaction();
|
||
|
try {
|
||
|
upgradeDatabase();
|
||
|
mDatabase.setTransactionSuccessful();
|
||
|
} finally {
|
||
|
mDatabase.endTransaction();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mDatabase != null) {
|
||
|
// use per table Mutex lock, turn off database lock, this
|
||
|
// improves performance as database's ReentrantLock is expansive
|
||
|
mDatabase.setLockingEnabled(false);
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
mCacheDatabase = context.openOrCreateDatabase(
|
||
|
CACHE_DATABASE_FILE, 0, null);
|
||
|
} catch (SQLiteException e) {
|
||
|
// try again by deleting the old db and create a new one
|
||
|
if (context.deleteDatabase(CACHE_DATABASE_FILE)) {
|
||
|
mCacheDatabase = context.openOrCreateDatabase(
|
||
|
CACHE_DATABASE_FILE, 0, null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// mCacheDatabase should not be null,
|
||
|
// the only case is RequestAPI test has problem to create db
|
||
|
if (mCacheDatabase != null
|
||
|
&& mCacheDatabase.getVersion() != CACHE_DATABASE_VERSION) {
|
||
|
mCacheDatabase.beginTransaction();
|
||
|
try {
|
||
|
upgradeCacheDatabase();
|
||
|
bootstrapCacheDatabase();
|
||
|
mCacheDatabase.setTransactionSuccessful();
|
||
|
} finally {
|
||
|
mCacheDatabase.endTransaction();
|
||
|
}
|
||
|
// Erase the files from the file system in the
|
||
|
// case that the database was updated and the
|
||
|
// there were existing cache content
|
||
|
CacheManager.removeAllCacheFiles();
|
||
|
}
|
||
|
|
||
|
if (mCacheDatabase != null) {
|
||
|
// use read_uncommitted to speed up READ
|
||
|
mCacheDatabase.execSQL("PRAGMA read_uncommitted = true;");
|
||
|
// as only READ can be called in the non-WebViewWorkerThread,
|
||
|
// and read_uncommitted is used, we can turn off database lock
|
||
|
// to use transaction.
|
||
|
mCacheDatabase.setLockingEnabled(false);
|
||
|
|
||
|
// use InsertHelper for faster insertion
|
||
|
mCacheInserter = new DatabaseUtils.InsertHelper(mCacheDatabase,
|
||
|
"cache");
|
||
|
mCacheUrlColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_URL_COL);
|
||
|
mCacheFilePathColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_FILE_PATH_COL);
|
||
|
mCacheLastModifyColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_LAST_MODIFY_COL);
|
||
|
mCacheETagColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_ETAG_COL);
|
||
|
mCacheExpiresColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_EXPIRES_COL);
|
||
|
mCacheExpiresStringColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_EXPIRES_STRING_COL);
|
||
|
mCacheMimeTypeColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_MIMETYPE_COL);
|
||
|
mCacheEncodingColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_ENCODING_COL);
|
||
|
mCacheHttpStatusColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_HTTP_STATUS_COL);
|
||
|
mCacheLocationColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_LOCATION_COL);
|
||
|
mCacheContentLengthColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_CONTENTLENGTH_COL);
|
||
|
mCacheContentDispositionColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_CONTENTDISPOSITION_COL);
|
||
|
mCacheCrossDomainColIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_CROSSDOMAIN_COL);
|
||
|
mCacheLastAccessTimeIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_LASTACCESSTIME_COL);
|
||
|
mCacheAccessCounterIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_ACCESSCOUNTER_COL);
|
||
|
mCacheWeightIndex = mCacheInserter
|
||
|
.getColumnIndex(CACHE_WEIGHT_COL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return mInstance;
|
||
|
}
|
||
|
|
||
|
private static void upgradeDatabase() {
|
||
|
int oldVersion = mDatabase.getVersion();
|
||
|
if (oldVersion != 0) {
|
||
|
Log.i(LOGTAG, "Upgrading database from version "
|
||
|
+ oldVersion + " to "
|
||
|
+ DATABASE_VERSION + ", which will destroy old data");
|
||
|
}
|
||
|
boolean justPasswords = 8 == oldVersion && 9 == DATABASE_VERSION;
|
||
|
boolean justAuth = 9 == oldVersion && 10 == DATABASE_VERSION;
|
||
|
boolean justTcpPreConn = 10 == oldVersion && 11 == DATABASE_VERSION;
|
||
|
if(justTcpPreConn) {
|
||
|
mDatabase.execSQL("DROP TABLE IF EXISTS "
|
||
|
+ mTableNames[TABLE_TCPPRECONNECTION_ID]);
|
||
|
mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_TCPPRECONNECTION_ID]
|
||
|
+ " (" + ID_COL + " INTEGER PRIMARY KEY, "
|
||
|
+ TCPPRECONNECTION_MAIN_URL_COL + " TEXT, "
|
||
|
+ TCPPRECONNECTION_MAIN_URL_USECOUNT_COL + " INTEGER, "
|
||
|
+ TCPPRECONNECTION_SUBHOST_COL + " TEXT,"
|
||
|
+ TCPPRECONNECTION_SUBHOST_USECOUNT_COL + " INTEGER, "
|
||
|
+ TCPPRECONNECTION_SUBHOST_WEIGHT_COL + " REAL" + ");");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (justAuth) {
|
||
|
mDatabase.execSQL("DROP TABLE IF EXISTS "
|
||
|
+ mTableNames[TABLE_HTTPAUTH_ID]);
|
||
|
mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID]
|
||
|
+ " (" + ID_COL + " INTEGER PRIMARY KEY, "
|
||
|
+ HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL
|
||
|
+ " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, "
|
||
|
+ HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE ("
|
||
|
+ HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL
|
||
|
+ ") ON CONFLICT REPLACE);");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!justPasswords) {
|
||
|
mDatabase.execSQL("DROP TABLE IF EXISTS "
|
||
|
+ mTableNames[TABLE_COOKIES_ID]);
|
||
|
mDatabase.execSQL("DROP TABLE IF EXISTS cache");
|
||
|
mDatabase.execSQL("DROP TABLE IF EXISTS "
|
||
|
+ mTableNames[TABLE_FORMURL_ID]);
|
||
|
mDatabase.execSQL("DROP TABLE IF EXISTS "
|
||
|
+ mTableNames[TABLE_FORMDATA_ID]);
|
||
|
mDatabase.execSQL("DROP TABLE IF EXISTS "
|
||
|
+ mTableNames[TABLE_HTTPAUTH_ID]);
|
||
|
mDatabase.execSQL("DROP TABLE IF EXISTS "
|
||
|
+ mTableNames[TABLE_TCPPRECONNECTION_ID]);
|
||
|
}
|
||
|
mDatabase.execSQL("DROP TABLE IF EXISTS "
|
||
|
+ mTableNames[TABLE_PASSWORD_ID]);
|
||
|
|
||
|
mDatabase.setVersion(DATABASE_VERSION);
|
||
|
|
||
|
if (!justPasswords) {
|
||
|
// cookies
|
||
|
mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_COOKIES_ID]
|
||
|
+ " (" + ID_COL + " INTEGER PRIMARY KEY, "
|
||
|
+ COOKIES_NAME_COL + " TEXT, " + COOKIES_VALUE_COL
|
||
|
+ " TEXT, " + COOKIES_DOMAIN_COL + " TEXT, "
|
||
|
+ COOKIES_PATH_COL + " TEXT, " + COOKIES_EXPIRES_COL
|
||
|
+ " INTEGER, " + COOKIES_SECURE_COL + " INTEGER" + ");");
|
||
|
mDatabase.execSQL("CREATE INDEX cookiesIndex ON "
|
||
|
+ mTableNames[TABLE_COOKIES_ID] + " (path)");
|
||
|
|
||
|
// formurl
|
||
|
mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMURL_ID]
|
||
|
+ " (" + ID_COL + " INTEGER PRIMARY KEY, " + FORMURL_URL_COL
|
||
|
+ " TEXT" + ");");
|
||
|
|
||
|
// formdata
|
||
|
mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMDATA_ID]
|
||
|
+ " (" + ID_COL + " INTEGER PRIMARY KEY, "
|
||
|
+ FORMDATA_URLID_COL + " INTEGER, " + FORMDATA_NAME_COL
|
||
|
+ " TEXT, " + FORMDATA_VALUE_COL + " TEXT," + " UNIQUE ("
|
||
|
+ FORMDATA_URLID_COL + ", " + FORMDATA_NAME_COL + ", "
|
||
|
+ FORMDATA_VALUE_COL + ") ON CONFLICT IGNORE);");
|
||
|
|
||
|
// httpauth
|
||
|
mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID]
|
||
|
+ " (" + ID_COL + " INTEGER PRIMARY KEY, "
|
||
|
+ HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL
|
||
|
+ " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, "
|
||
|
+ HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE ("
|
||
|
+ HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL
|
||
|
+ ") ON CONFLICT REPLACE);");
|
||
|
|
||
|
// pre connection historys
|
||
|
Log.v(LOGTAG, "TCP pre connection: creating table in database");
|
||
|
mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_TCPPRECONNECTION_ID]
|
||
|
+ " (" + ID_COL + " INTEGER PRIMARY KEY, "
|
||
|
+ TCPPRECONNECTION_MAIN_URL_COL + " TEXT, "
|
||
|
+ TCPPRECONNECTION_MAIN_URL_USECOUNT_COL + " INTEGER, "
|
||
|
+ TCPPRECONNECTION_SUBHOST_COL + " TEXT,"
|
||
|
+ TCPPRECONNECTION_SUBHOST_USECOUNT_COL + " INTEGER, "
|
||
|
+ TCPPRECONNECTION_SUBHOST_WEIGHT_COL + " REAL" + ");");
|
||
|
}
|
||
|
// passwords
|
||
|
mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_PASSWORD_ID]
|
||
|
+ " (" + ID_COL + " INTEGER PRIMARY KEY, "
|
||
|
+ PASSWORD_HOST_COL + " TEXT, " + PASSWORD_USERNAME_COL
|
||
|
+ " TEXT, " + PASSWORD_PASSWORD_COL + " TEXT," + " UNIQUE ("
|
||
|
+ PASSWORD_HOST_COL + ", " + PASSWORD_USERNAME_COL
|
||
|
+ ") ON CONFLICT REPLACE);");
|
||
|
}
|
||
|
|
||
|
private static void upgradeCacheDatabase() {
|
||
|
int oldVersion = mCacheDatabase.getVersion();
|
||
|
if (oldVersion != 0) {
|
||
|
Log.i(LOGTAG, "Upgrading cache database from version "
|
||
|
+ oldVersion + " to "
|
||
|
+ DATABASE_VERSION + ", which will destroy all old data");
|
||
|
}
|
||
|
mCacheDatabase.execSQL("DROP TABLE IF EXISTS cache");
|
||
|
mCacheDatabase.setVersion(CACHE_DATABASE_VERSION);
|
||
|
}
|
||
|
|
||
|
private static void bootstrapCacheDatabase() {
|
||
|
if (mCacheDatabase != null) {
|
||
|
mCacheDatabase.execSQL("CREATE TABLE cache"
|
||
|
+ " (" + ID_COL + " INTEGER PRIMARY KEY, " + CACHE_URL_COL
|
||
|
+ " TEXT, " + CACHE_FILE_PATH_COL + " TEXT, "
|
||
|
+ CACHE_LAST_MODIFY_COL + " TEXT, " + CACHE_ETAG_COL
|
||
|
+ " TEXT, " + CACHE_EXPIRES_COL + " INTEGER, "
|
||
|
+ CACHE_EXPIRES_STRING_COL + " TEXT, "
|
||
|
+ CACHE_MIMETYPE_COL + " TEXT, " + CACHE_ENCODING_COL
|
||
|
+ " TEXT," + CACHE_HTTP_STATUS_COL + " INTEGER, "
|
||
|
+ CACHE_LOCATION_COL + " TEXT, " + CACHE_CONTENTLENGTH_COL
|
||
|
+ " INTEGER, " + CACHE_CONTENTDISPOSITION_COL + " TEXT, "
|
||
|
+ CACHE_CROSSDOMAIN_COL + " TEXT,"
|
||
|
+ CACHE_LASTACCESSTIME_COL + " INTEGER,"
|
||
|
+ CACHE_ACCESSCOUNTER_COL + " INTEGER,"
|
||
|
+ CACHE_WEIGHT_COL + " INTEGER,"
|
||
|
+ " UNIQUE (" + CACHE_URL_COL + ") ON CONFLICT REPLACE);");
|
||
|
mCacheDatabase.execSQL("CREATE INDEX cacheUrlIndex ON cache ("
|
||
|
+ CACHE_URL_COL + ")");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private boolean hasEntries(int tableId) {
|
||
|
if (mDatabase == null) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Cursor cursor = null;
|
||
|
boolean ret = false;
|
||
|
try {
|
||
|
cursor = mDatabase.query(mTableNames[tableId], ID_PROJECTION,
|
||
|
null, null, null, null, null);
|
||
|
ret = cursor.moveToFirst() == true;
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "hasEntries", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// cookies functions
|
||
|
//
|
||
|
|
||
|
/**
|
||
|
* Get cookies in the format of CookieManager.Cookie inside an ArrayList for
|
||
|
* a given domain
|
||
|
*
|
||
|
* @return ArrayList<Cookie> If nothing is found, return an empty list.
|
||
|
*/
|
||
|
ArrayList<Cookie> getCookiesForDomain(String domain) {
|
||
|
ArrayList<Cookie> list = new ArrayList<Cookie>();
|
||
|
if (domain == null || mDatabase == null) {
|
||
|
return list;
|
||
|
}
|
||
|
|
||
|
synchronized (mCookieLock) {
|
||
|
final String[] columns = new String[] {
|
||
|
ID_COL, COOKIES_DOMAIN_COL, COOKIES_PATH_COL,
|
||
|
COOKIES_NAME_COL, COOKIES_VALUE_COL, COOKIES_EXPIRES_COL,
|
||
|
COOKIES_SECURE_COL
|
||
|
};
|
||
|
final String selection = "(" + COOKIES_DOMAIN_COL
|
||
|
+ " GLOB '*' || ?)";
|
||
|
Cursor cursor = null;
|
||
|
try {
|
||
|
cursor = mDatabase.query(mTableNames[TABLE_COOKIES_ID],
|
||
|
columns, selection, new String[] { domain }, null, null,
|
||
|
null);
|
||
|
if (cursor.moveToFirst()) {
|
||
|
int domainCol = cursor.getColumnIndex(COOKIES_DOMAIN_COL);
|
||
|
int pathCol = cursor.getColumnIndex(COOKIES_PATH_COL);
|
||
|
int nameCol = cursor.getColumnIndex(COOKIES_NAME_COL);
|
||
|
int valueCol = cursor.getColumnIndex(COOKIES_VALUE_COL);
|
||
|
int expiresCol = cursor.getColumnIndex(COOKIES_EXPIRES_COL);
|
||
|
int secureCol = cursor.getColumnIndex(COOKIES_SECURE_COL);
|
||
|
do {
|
||
|
Cookie cookie = new Cookie();
|
||
|
cookie.domain = cursor.getString(domainCol);
|
||
|
cookie.path = cursor.getString(pathCol);
|
||
|
cookie.name = cursor.getString(nameCol);
|
||
|
cookie.value = cursor.getString(valueCol);
|
||
|
if (cursor.isNull(expiresCol)) {
|
||
|
cookie.expires = -1;
|
||
|
} else {
|
||
|
cookie.expires = cursor.getLong(expiresCol);
|
||
|
}
|
||
|
cookie.secure = cursor.getShort(secureCol) != 0;
|
||
|
cookie.mode = Cookie.MODE_NORMAL;
|
||
|
list.add(cookie);
|
||
|
} while (cursor.moveToNext());
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "getCookiesForDomain", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
return list;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Delete cookies which matches (domain, path, name).
|
||
|
*
|
||
|
* @param domain If it is null, nothing happens.
|
||
|
* @param path If it is null, all the cookies match (domain) will be
|
||
|
* deleted.
|
||
|
* @param name If it is null, all the cookies match (domain, path) will be
|
||
|
* deleted.
|
||
|
*/
|
||
|
void deleteCookies(String domain, String path, String name) {
|
||
|
if (domain == null || mDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
synchronized (mCookieLock) {
|
||
|
final String where = "(" + COOKIES_DOMAIN_COL + " == ?) AND ("
|
||
|
+ COOKIES_PATH_COL + " == ?) AND (" + COOKIES_NAME_COL
|
||
|
+ " == ?)";
|
||
|
mDatabase.delete(mTableNames[TABLE_COOKIES_ID], where,
|
||
|
new String[] { domain, path, name });
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add a cookie to the database
|
||
|
*
|
||
|
* @param cookie
|
||
|
*/
|
||
|
void addCookie(Cookie cookie) {
|
||
|
if (cookie.domain == null || cookie.path == null || cookie.name == null
|
||
|
|| mDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
synchronized (mCookieLock) {
|
||
|
ContentValues cookieVal = new ContentValues();
|
||
|
cookieVal.put(COOKIES_DOMAIN_COL, cookie.domain);
|
||
|
cookieVal.put(COOKIES_PATH_COL, cookie.path);
|
||
|
cookieVal.put(COOKIES_NAME_COL, cookie.name);
|
||
|
cookieVal.put(COOKIES_VALUE_COL, cookie.value);
|
||
|
if (cookie.expires != -1) {
|
||
|
cookieVal.put(COOKIES_EXPIRES_COL, cookie.expires);
|
||
|
}
|
||
|
cookieVal.put(COOKIES_SECURE_COL, cookie.secure);
|
||
|
try {
|
||
|
mDatabase.insert(mTableNames[TABLE_COOKIES_ID], null, cookieVal);
|
||
|
} catch (Exception e) {
|
||
|
Log.e(LOGTAG, "addCookie", e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Whether there is any cookies in the database
|
||
|
*
|
||
|
* @return TRUE if there is cookie.
|
||
|
*/
|
||
|
boolean hasCookies() {
|
||
|
synchronized (mCookieLock) {
|
||
|
return hasEntries(TABLE_COOKIES_ID);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear cookie database
|
||
|
*/
|
||
|
void clearCookies() {
|
||
|
if (mDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
synchronized (mCookieLock) {
|
||
|
mDatabase.delete(mTableNames[TABLE_COOKIES_ID], null, null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear session cookies, which means cookie doesn't have EXPIRES.
|
||
|
*/
|
||
|
void clearSessionCookies() {
|
||
|
if (mDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
final String sessionExpired = COOKIES_EXPIRES_COL + " ISNULL";
|
||
|
synchronized (mCookieLock) {
|
||
|
mDatabase.delete(mTableNames[TABLE_COOKIES_ID], sessionExpired,
|
||
|
null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear expired cookies
|
||
|
*
|
||
|
* @param now Time for now
|
||
|
*/
|
||
|
void clearExpiredCookies(long now) {
|
||
|
if (mDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
final String expires = COOKIES_EXPIRES_COL + " <= ?";
|
||
|
synchronized (mCookieLock) {
|
||
|
mDatabase.delete(mTableNames[TABLE_COOKIES_ID], expires,
|
||
|
new String[] { Long.toString(now) });
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// cache functions
|
||
|
//
|
||
|
|
||
|
// only called from WebViewWorkerThread
|
||
|
boolean startCacheTransaction() {
|
||
|
if (++mCacheTransactionRefcount == 1) {
|
||
|
if (!Thread.currentThread().equals(
|
||
|
WebViewWorker.getHandler().getLooper().getThread())) {
|
||
|
Log.w(LOGTAG, "startCacheTransaction should be called from "
|
||
|
+ "WebViewWorkerThread instead of from "
|
||
|
+ Thread.currentThread().getName());
|
||
|
}
|
||
|
mCacheDatabase.beginTransaction();
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// only called from WebViewWorkerThread
|
||
|
boolean endCacheTransaction() {
|
||
|
if (--mCacheTransactionRefcount == 0) {
|
||
|
if (!Thread.currentThread().equals(
|
||
|
WebViewWorker.getHandler().getLooper().getThread())) {
|
||
|
Log.w(LOGTAG, "endCacheTransaction should be called from "
|
||
|
+ "WebViewWorkerThread instead of from "
|
||
|
+ Thread.currentThread().getName());
|
||
|
}
|
||
|
try {
|
||
|
mCacheDatabase.setTransactionSuccessful();
|
||
|
} finally {
|
||
|
mCacheDatabase.endTransaction();
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Update cache statistics
|
||
|
*/
|
||
|
CacheAccessStat updateCacheStat(String url, CacheResult c) {
|
||
|
if (c.expires != 0) {
|
||
|
CacheAccessStat cacheStat;
|
||
|
synchronized (mCacheStatLock) {
|
||
|
cacheStat = mCacheStat.get(url);
|
||
|
if (cacheStat == null) {
|
||
|
cacheStat = new CacheAccessStat(c);
|
||
|
mCacheStat.put( url , cacheStat);
|
||
|
} else {
|
||
|
cacheStat.hit();
|
||
|
}
|
||
|
}
|
||
|
return cacheStat;
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Flush cache statistics
|
||
|
*/
|
||
|
void flushCacheStat() {
|
||
|
synchronized (mCacheStatLock) {
|
||
|
if (!mCacheStat.isEmpty()) {
|
||
|
long start = System.currentTimeMillis();
|
||
|
for (Map.Entry<String, CacheAccessStat> entry : mCacheStat.entrySet()) {
|
||
|
mCacheDatabase.execSQL("UPDATE cache SET " +
|
||
|
CACHE_LASTACCESSTIME_COL + "=" + entry.getValue().mLastAccessTime + "," +
|
||
|
CACHE_ACCESSCOUNTER_COL + "=" + entry.getValue().mCacheAccessCounter + "," +
|
||
|
CACHE_WEIGHT_COL + "=" + entry.getValue().mWeight + " WHERE url = ?",
|
||
|
new String[] { entry.getKey() } );
|
||
|
}
|
||
|
mCacheStat.clear();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get a cache item.
|
||
|
*
|
||
|
* @param url The url
|
||
|
* @return CacheResult The CacheManager.CacheResult
|
||
|
*/
|
||
|
CacheResult getCache(String url) {
|
||
|
if (url == null || mCacheDatabase == null) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
Cursor cursor = null;
|
||
|
final String query = "SELECT filepath, lastmodify, etag, expires, "
|
||
|
+ "expiresstring, mimetype, encoding, httpstatus, location, contentlength, "
|
||
|
+ "contentdisposition, crossdomain, accesscounter FROM cache WHERE url = ?";
|
||
|
try {
|
||
|
cursor = mCacheDatabase.rawQuery(query, new String[] { url });
|
||
|
if (cursor.moveToFirst()) {
|
||
|
CacheResult ret = new CacheResult();
|
||
|
ret.localPath = cursor.getString(0);
|
||
|
ret.lastModified = cursor.getString(1);
|
||
|
ret.etag = cursor.getString(2);
|
||
|
ret.expires = cursor.getLong(3);
|
||
|
ret.expiresString = cursor.getString(4);
|
||
|
ret.mimeType = cursor.getString(5);
|
||
|
ret.encoding = cursor.getString(6);
|
||
|
ret.httpStatusCode = cursor.getInt(7);
|
||
|
ret.location = cursor.getString(8);
|
||
|
ret.contentLength = cursor.getLong(9);
|
||
|
ret.contentdisposition = cursor.getString(10);
|
||
|
ret.crossDomain = cursor.getString(11);
|
||
|
ret.accessCounter = cursor.getInt(12);
|
||
|
updateCacheStat(url, ret);
|
||
|
return ret;
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "getCache", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Remove a cache item.
|
||
|
*
|
||
|
* @param url The url
|
||
|
*/
|
||
|
void removeCache(String url) {
|
||
|
if (url == null || mCacheDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
mCacheDatabase.execSQL("DELETE FROM cache WHERE url = ?", new String[] { url });
|
||
|
synchronized (mCacheStatLock) {
|
||
|
mCacheStat.remove(url);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add or update a cache. CACHE_URL_COL is unique in the table.
|
||
|
*
|
||
|
* @param url The url
|
||
|
* @param c The CacheManager.CacheResult
|
||
|
*/
|
||
|
void addCache(String url, CacheResult c) {
|
||
|
if (url == null || mCacheDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
mCacheInserter.prepareForInsert();
|
||
|
mCacheInserter.bind(mCacheUrlColIndex, url);
|
||
|
mCacheInserter.bind(mCacheFilePathColIndex, c.localPath);
|
||
|
mCacheInserter.bind(mCacheLastModifyColIndex, c.lastModified);
|
||
|
mCacheInserter.bind(mCacheETagColIndex, c.etag);
|
||
|
mCacheInserter.bind(mCacheExpiresColIndex, c.expires);
|
||
|
mCacheInserter.bind(mCacheExpiresStringColIndex, c.expiresString);
|
||
|
mCacheInserter.bind(mCacheMimeTypeColIndex, c.mimeType);
|
||
|
mCacheInserter.bind(mCacheEncodingColIndex, c.encoding);
|
||
|
mCacheInserter.bind(mCacheHttpStatusColIndex, c.httpStatusCode);
|
||
|
mCacheInserter.bind(mCacheLocationColIndex, c.location);
|
||
|
mCacheInserter.bind(mCacheContentLengthColIndex, c.contentLength);
|
||
|
mCacheInserter.bind(mCacheContentDispositionColIndex,
|
||
|
c.contentdisposition);
|
||
|
mCacheInserter.bind(mCacheCrossDomainColIndex, c.crossDomain);
|
||
|
|
||
|
CacheAccessStat cacheStat = updateCacheStat(url, c);
|
||
|
if (cacheStat == null) {
|
||
|
mCacheInserter.bind(mCacheLastAccessTimeIndex, System.currentTimeMillis());
|
||
|
mCacheInserter.bind(mCacheAccessCounterIndex, c.accessCounter+1);
|
||
|
mCacheInserter.bind(mCacheWeightIndex, 0);
|
||
|
}
|
||
|
else {
|
||
|
mCacheInserter.bind(mCacheLastAccessTimeIndex, cacheStat.mLastAccessTime);
|
||
|
mCacheInserter.bind(mCacheAccessCounterIndex, cacheStat.mCacheAccessCounter);
|
||
|
mCacheInserter.bind(mCacheWeightIndex, cacheStat.mWeight);
|
||
|
}
|
||
|
mCacheInserter.execute();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear cache database
|
||
|
*/
|
||
|
void clearCache() {
|
||
|
if (mCacheDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
mCacheDatabase.delete("cache", null, null);
|
||
|
synchronized (mCacheStatLock) {
|
||
|
mCacheStat.clear();
|
||
|
}
|
||
|
|
||
|
synchronized (mTcpPreConnectionLock) {
|
||
|
mDatabase.delete(mTableNames[TABLE_TCPPRECONNECTION_ID], null, null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
boolean hasCache() {
|
||
|
if (mCacheDatabase == null) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Cursor cursor = null;
|
||
|
boolean ret = false;
|
||
|
try {
|
||
|
cursor = mCacheDatabase.query("cache", ID_PROJECTION,
|
||
|
null, null, null, null, null);
|
||
|
ret = cursor.moveToFirst() == true;
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "hasCache", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
long getCacheTotalSize() {
|
||
|
if (mCacheDatabase == null) {
|
||
|
return 0;
|
||
|
}
|
||
|
long size = 0;
|
||
|
Cursor cursor = null;
|
||
|
final String query = "SELECT SUM(contentlength) as sum FROM cache";
|
||
|
try {
|
||
|
cursor = mCacheDatabase.rawQuery(query, null);
|
||
|
if (cursor.moveToFirst()) {
|
||
|
size = cursor.getLong(0);
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "getCacheTotalSize", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
return size;
|
||
|
}
|
||
|
|
||
|
List<String> trimCache(long amount) {
|
||
|
ArrayList<String> pathList = new ArrayList<String>(100);
|
||
|
Cursor cursor = null;
|
||
|
flushCacheStat();
|
||
|
if (CACHE_EVICT_EXPIRED!=0) {
|
||
|
mCacheDatabase.execSQL("UPDATE cache SET " + CACHE_WEIGHT_COL + "=" + CACHE_LASTACCESSTIME_COL +
|
||
|
" WHERE " + CACHE_EXPIRES_COL + "<=" + System.currentTimeMillis() +
|
||
|
" AND " + CACHE_EXPIRES_COL + "!=0");
|
||
|
}
|
||
|
final String query = "SELECT contentlength, filepath FROM cache ORDER BY " + CACHE_ORDER_BY + " ASC";
|
||
|
try {
|
||
|
cursor = mCacheDatabase.rawQuery(query, null);
|
||
|
if (cursor.moveToFirst()) {
|
||
|
int batchSize = 100;
|
||
|
StringBuilder pathStr = new StringBuilder(20 + 16 * batchSize);
|
||
|
pathStr.append("DELETE FROM cache WHERE filepath IN (?");
|
||
|
for (int i = 1; i < batchSize; i++) {
|
||
|
pathStr.append(", ?");
|
||
|
}
|
||
|
pathStr.append(")");
|
||
|
SQLiteStatement statement = null;
|
||
|
try {
|
||
|
statement = mCacheDatabase.compileStatement(
|
||
|
pathStr.toString());
|
||
|
// as bindString() uses 1-based index, initialize index to 1
|
||
|
int index = 1;
|
||
|
do {
|
||
|
long length = cursor.getLong(0);
|
||
|
if (length == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
amount -= length;
|
||
|
String filePath = cursor.getString(1);
|
||
|
statement.bindString(index, filePath);
|
||
|
pathList.add(filePath);
|
||
|
if (index++ == batchSize) {
|
||
|
statement.execute();
|
||
|
statement.clearBindings();
|
||
|
index = 1;
|
||
|
}
|
||
|
} while (cursor.moveToNext() && amount > 0);
|
||
|
if (index > 1) {
|
||
|
// there may be old bindings from the previous statement
|
||
|
// if index is less than batchSize, which is Ok.
|
||
|
statement.execute();
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "trimCache SQLiteStatement", e);
|
||
|
} finally {
|
||
|
if (statement != null) statement.close();
|
||
|
}
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "trimCache Cursor", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
return pathList;
|
||
|
}
|
||
|
|
||
|
List<String> getAllCacheFileNames() {
|
||
|
ArrayList<String> pathList = null;
|
||
|
Cursor cursor = null;
|
||
|
try {
|
||
|
cursor = mCacheDatabase.rawQuery("SELECT filepath FROM cache",
|
||
|
null);
|
||
|
if (cursor != null && cursor.moveToFirst()) {
|
||
|
pathList = new ArrayList<String>(cursor.getCount());
|
||
|
do {
|
||
|
pathList.add(cursor.getString(0));
|
||
|
} while (cursor.moveToNext());
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "getAllCacheFileNames", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
return pathList;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// password functions
|
||
|
//
|
||
|
|
||
|
/**
|
||
|
* Set password. Tuple (PASSWORD_HOST_COL, PASSWORD_USERNAME_COL) is unique.
|
||
|
*
|
||
|
* @param schemePlusHost The scheme and host for the password
|
||
|
* @param username The username for the password. If it is null, it means
|
||
|
* password can't be saved.
|
||
|
* @param password The password
|
||
|
*/
|
||
|
void setUsernamePassword(String schemePlusHost, String username,
|
||
|
String password) {
|
||
|
if (schemePlusHost == null || mDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
synchronized (mPasswordLock) {
|
||
|
final ContentValues c = new ContentValues();
|
||
|
c.put(PASSWORD_HOST_COL, schemePlusHost);
|
||
|
c.put(PASSWORD_USERNAME_COL, username);
|
||
|
c.put(PASSWORD_PASSWORD_COL, password);
|
||
|
try {
|
||
|
mDatabase.insert(mTableNames[TABLE_PASSWORD_ID],
|
||
|
PASSWORD_HOST_COL, c);
|
||
|
} catch (Exception e) {
|
||
|
Log.e(LOGTAG, "setUsernamePassword", e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the username and password for a given host
|
||
|
*
|
||
|
* @param schemePlusHost The scheme and host which passwords applies to
|
||
|
* @return String[] if found, String[0] is username, which can be null and
|
||
|
* String[1] is password. Return null if it can't find anything.
|
||
|
*/
|
||
|
String[] getUsernamePassword(String schemePlusHost) {
|
||
|
if (schemePlusHost == null || mDatabase == null) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
final String[] columns = new String[] {
|
||
|
PASSWORD_USERNAME_COL, PASSWORD_PASSWORD_COL
|
||
|
};
|
||
|
final String selection = "(" + PASSWORD_HOST_COL + " == ?)";
|
||
|
synchronized (mPasswordLock) {
|
||
|
String[] ret = null;
|
||
|
Cursor cursor = null;
|
||
|
try {
|
||
|
cursor = mDatabase.query(mTableNames[TABLE_PASSWORD_ID],
|
||
|
columns, selection, new String[] { schemePlusHost }, null,
|
||
|
null, null);
|
||
|
if (cursor.moveToFirst()) {
|
||
|
ret = new String[2];
|
||
|
ret[0] = cursor.getString(
|
||
|
cursor.getColumnIndex(PASSWORD_USERNAME_COL));
|
||
|
ret[1] = cursor.getString(
|
||
|
cursor.getColumnIndex(PASSWORD_PASSWORD_COL));
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "getUsernamePassword", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Find out if there are any passwords saved.
|
||
|
*
|
||
|
* @return TRUE if there is passwords saved
|
||
|
*/
|
||
|
public boolean hasUsernamePassword() {
|
||
|
synchronized (mPasswordLock) {
|
||
|
return hasEntries(TABLE_PASSWORD_ID);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear password database
|
||
|
*/
|
||
|
public void clearUsernamePassword() {
|
||
|
if (mDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
synchronized (mPasswordLock) {
|
||
|
mDatabase.delete(mTableNames[TABLE_PASSWORD_ID], null, null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// http authentication password functions
|
||
|
//
|
||
|
|
||
|
/**
|
||
|
* Set HTTP authentication password. Tuple (HTTPAUTH_HOST_COL,
|
||
|
* HTTPAUTH_REALM_COL, HTTPAUTH_USERNAME_COL) is unique.
|
||
|
*
|
||
|
* @param host The host for the password
|
||
|
* @param realm The realm for the password
|
||
|
* @param username The username for the password. If it is null, it means
|
||
|
* password can't be saved.
|
||
|
* @param password The password
|
||
|
*/
|
||
|
void setHttpAuthUsernamePassword(String host, String realm, String username,
|
||
|
String password) {
|
||
|
if (host == null || realm == null || mDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
synchronized (mHttpAuthLock) {
|
||
|
final ContentValues c = new ContentValues();
|
||
|
c.put(HTTPAUTH_HOST_COL, host);
|
||
|
c.put(HTTPAUTH_REALM_COL, realm);
|
||
|
c.put(HTTPAUTH_USERNAME_COL, username);
|
||
|
c.put(HTTPAUTH_PASSWORD_COL, password);
|
||
|
try {
|
||
|
mDatabase.insert(mTableNames[TABLE_HTTPAUTH_ID],
|
||
|
HTTPAUTH_HOST_COL, c);
|
||
|
} catch (Exception e) {
|
||
|
Log.e(LOGTAG, "setHttpAuthUsernamePassword", e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the HTTP authentication username and password for a given
|
||
|
* host+realm pair
|
||
|
*
|
||
|
* @param host The host the password applies to
|
||
|
* @param realm The realm the password applies to
|
||
|
* @return String[] if found, String[0] is username, which can be null and
|
||
|
* String[1] is password. Return null if it can't find anything.
|
||
|
*/
|
||
|
String[] getHttpAuthUsernamePassword(String host, String realm) {
|
||
|
if (host == null || realm == null || mDatabase == null){
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
final String[] columns = new String[] {
|
||
|
HTTPAUTH_USERNAME_COL, HTTPAUTH_PASSWORD_COL
|
||
|
};
|
||
|
final String selection = "(" + HTTPAUTH_HOST_COL + " == ?) AND ("
|
||
|
+ HTTPAUTH_REALM_COL + " == ?)";
|
||
|
synchronized (mHttpAuthLock) {
|
||
|
String[] ret = null;
|
||
|
Cursor cursor = null;
|
||
|
try {
|
||
|
cursor = mDatabase.query(mTableNames[TABLE_HTTPAUTH_ID],
|
||
|
columns, selection, new String[] { host, realm }, null,
|
||
|
null, null);
|
||
|
if (cursor.moveToFirst()) {
|
||
|
ret = new String[2];
|
||
|
ret[0] = cursor.getString(
|
||
|
cursor.getColumnIndex(HTTPAUTH_USERNAME_COL));
|
||
|
ret[1] = cursor.getString(
|
||
|
cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL));
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "getHttpAuthUsernamePassword", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Find out if there are any HTTP authentication passwords saved.
|
||
|
*
|
||
|
* @return TRUE if there are passwords saved
|
||
|
*/
|
||
|
public boolean hasHttpAuthUsernamePassword() {
|
||
|
synchronized (mHttpAuthLock) {
|
||
|
return hasEntries(TABLE_HTTPAUTH_ID);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear HTTP authentication password database
|
||
|
*/
|
||
|
public void clearHttpAuthUsernamePassword() {
|
||
|
if (mDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
synchronized (mHttpAuthLock) {
|
||
|
mDatabase.delete(mTableNames[TABLE_HTTPAUTH_ID], null, null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// form data functions
|
||
|
//
|
||
|
|
||
|
/**
|
||
|
* Set form data for a site. Tuple (FORMDATA_URLID_COL, FORMDATA_NAME_COL,
|
||
|
* FORMDATA_VALUE_COL) is unique
|
||
|
*
|
||
|
* @param url The url of the site
|
||
|
* @param formdata The form data in HashMap
|
||
|
*/
|
||
|
void setFormData(String url, HashMap<String, String> formdata) {
|
||
|
if (url == null || formdata == null || mDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
final String selection = "(" + FORMURL_URL_COL + " == ?)";
|
||
|
synchronized (mFormLock) {
|
||
|
long urlid = -1;
|
||
|
Cursor cursor = null;
|
||
|
try {
|
||
|
cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID],
|
||
|
ID_PROJECTION, selection, new String[] { url }, null, null,
|
||
|
null);
|
||
|
if (cursor.moveToFirst()) {
|
||
|
urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
|
||
|
} else {
|
||
|
ContentValues c = new ContentValues();
|
||
|
c.put(FORMURL_URL_COL, url);
|
||
|
urlid = mDatabase.insert(
|
||
|
mTableNames[TABLE_FORMURL_ID], null, c);
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "setFormData", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
if (urlid >= 0) {
|
||
|
Set<Entry<String, String>> set = formdata.entrySet();
|
||
|
Iterator<Entry<String, String>> iter = set.iterator();
|
||
|
ContentValues map = new ContentValues();
|
||
|
map.put(FORMDATA_URLID_COL, urlid);
|
||
|
while (iter.hasNext()) {
|
||
|
Entry<String, String> entry = iter.next();
|
||
|
map.put(FORMDATA_NAME_COL, entry.getKey());
|
||
|
map.put(FORMDATA_VALUE_COL, entry.getValue());
|
||
|
try {
|
||
|
mDatabase.insert(mTableNames[TABLE_FORMDATA_ID], null,
|
||
|
map);
|
||
|
} catch (Exception e) {
|
||
|
Log.e(LOGTAG, "setFormData", e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get all the values for a form entry with "name" in a given site
|
||
|
*
|
||
|
* @param url The url of the site
|
||
|
* @param name The name of the form entry
|
||
|
* @return A list of values. Return empty list if nothing is found.
|
||
|
*/
|
||
|
ArrayList<String> getFormData(String url, String name) {
|
||
|
ArrayList<String> values = new ArrayList<String>();
|
||
|
if (url == null || name == null || mDatabase == null) {
|
||
|
return values;
|
||
|
}
|
||
|
|
||
|
final String urlSelection = "(" + FORMURL_URL_COL + " == ?)";
|
||
|
final String dataSelection = "(" + FORMDATA_URLID_COL + " == ?) AND ("
|
||
|
+ FORMDATA_NAME_COL + " == ?)";
|
||
|
synchronized (mFormLock) {
|
||
|
Cursor cursor = null;
|
||
|
try {
|
||
|
cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID],
|
||
|
ID_PROJECTION, urlSelection, new String[] { url }, null,
|
||
|
null, null);
|
||
|
if (cursor.moveToFirst()) {
|
||
|
long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL));
|
||
|
Cursor dataCursor = null;
|
||
|
try {
|
||
|
dataCursor = mDatabase.query(
|
||
|
mTableNames[TABLE_FORMDATA_ID],
|
||
|
new String[] { ID_COL, FORMDATA_VALUE_COL },
|
||
|
dataSelection,
|
||
|
new String[] { Long.toString(urlid), name },
|
||
|
null, null, null);
|
||
|
if (dataCursor.moveToFirst()) {
|
||
|
int valueCol = dataCursor.getColumnIndex(
|
||
|
FORMDATA_VALUE_COL);
|
||
|
do {
|
||
|
values.add(dataCursor.getString(valueCol));
|
||
|
} while (dataCursor.moveToNext());
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "getFormData dataCursor", e);
|
||
|
} finally {
|
||
|
if (dataCursor != null) dataCursor.close();
|
||
|
}
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "getFormData cursor", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
return values;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Find out if there is form data saved.
|
||
|
*
|
||
|
* @return TRUE if there is form data in the database
|
||
|
*/
|
||
|
public boolean hasFormData() {
|
||
|
synchronized (mFormLock) {
|
||
|
return hasEntries(TABLE_FORMURL_ID);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Clear form database
|
||
|
*/
|
||
|
public void clearFormData() {
|
||
|
if (mDatabase == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
synchronized (mFormLock) {
|
||
|
mDatabase.delete(mTableNames[TABLE_FORMURL_ID], null, null);
|
||
|
mDatabase.delete(mTableNames[TABLE_FORMDATA_ID], null, null);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// tcp pre connection functions
|
||
|
//
|
||
|
|
||
|
/**
|
||
|
* Write learnt history to db
|
||
|
*/
|
||
|
public void setSubhostsData(SubResourcesHistory subResourcesHistory)
|
||
|
{
|
||
|
if (null == subResourcesHistory || null == mDatabase) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// delete old data
|
||
|
synchronized (mTcpPreConnectionLock) {
|
||
|
mDatabase.delete(mTableNames[TABLE_TCPPRECONNECTION_ID], null, null);
|
||
|
}
|
||
|
|
||
|
// save new data
|
||
|
Set mainUrls;
|
||
|
synchronized (SubResourcesHistory.mSubResourcesHistoryLock) {
|
||
|
mainUrls = new HashSet(subResourcesHistory.getMainUrls());
|
||
|
}
|
||
|
|
||
|
if (mainUrls.isEmpty ()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
Iterator mainUrlsIterator = mainUrls.iterator();
|
||
|
|
||
|
while (mainUrlsIterator.hasNext()) {
|
||
|
//
|
||
|
String mainUrl = (String)(mainUrlsIterator.next());
|
||
|
int mainUrlUseCount = subResourcesHistory.getUseCount(mainUrl);
|
||
|
|
||
|
//
|
||
|
LinkedList<Subhost> subHosts = subResourcesHistory.getSubHosts(mainUrl);
|
||
|
if (null != subHosts) {
|
||
|
|
||
|
ListIterator subHostsIterator;
|
||
|
synchronized (SubResourcesHistory.mSubResourcesHistoryLock) {
|
||
|
subHostsIterator = ((LinkedList<Subhost>)(subHosts.clone())).listIterator();
|
||
|
}
|
||
|
|
||
|
while (subHostsIterator.hasNext()) {
|
||
|
Subhost subhost = (Subhost)subHostsIterator.next();
|
||
|
String subHostUrl = subhost.getHost();
|
||
|
int subHostUseCount = subhost.getNumberOfReferences();
|
||
|
double subHostWeight = subhost.getWeight();
|
||
|
|
||
|
synchronized (mTcpPreConnectionLock) {
|
||
|
|
||
|
final ContentValues conValues = new ContentValues();
|
||
|
conValues.put(TCPPRECONNECTION_MAIN_URL_COL, mainUrl);
|
||
|
conValues.put(TCPPRECONNECTION_MAIN_URL_USECOUNT_COL, mainUrlUseCount);
|
||
|
conValues.put(TCPPRECONNECTION_SUBHOST_COL, subHostUrl);
|
||
|
conValues.put(TCPPRECONNECTION_SUBHOST_USECOUNT_COL, subHostUseCount);
|
||
|
conValues.put(TCPPRECONNECTION_SUBHOST_WEIGHT_COL, subHostWeight);
|
||
|
|
||
|
try {
|
||
|
mDatabase.insert(mTableNames[TABLE_TCPPRECONNECTION_ID],
|
||
|
TCPPRECONNECTION_SUBHOST_USECOUNT_COL, conValues);
|
||
|
} catch (Exception e) {
|
||
|
Log.e(LOGTAG, "setTcpPreConnection", e);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
*/
|
||
|
public void getSubhostsData(SubResourcesHistory subResourcesHistory)
|
||
|
{
|
||
|
if (null == subResourcesHistory || null == mDatabase) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
synchronized (mTcpPreConnectionLock) {
|
||
|
final String[] columns = new String[] {
|
||
|
TCPPRECONNECTION_MAIN_URL_COL,
|
||
|
TCPPRECONNECTION_MAIN_URL_USECOUNT_COL,
|
||
|
TCPPRECONNECTION_SUBHOST_COL,
|
||
|
TCPPRECONNECTION_SUBHOST_USECOUNT_COL,
|
||
|
TCPPRECONNECTION_SUBHOST_WEIGHT_COL
|
||
|
};
|
||
|
|
||
|
Cursor cursor = null;
|
||
|
|
||
|
try {
|
||
|
cursor = mDatabase.query(mTableNames[TABLE_TCPPRECONNECTION_ID],
|
||
|
columns, null, null, null, null, null);
|
||
|
if (cursor.moveToFirst()) {
|
||
|
int mainUrlCol = cursor.getColumnIndex(TCPPRECONNECTION_MAIN_URL_COL);
|
||
|
int mainUrlUseCountCol = cursor.getColumnIndex(TCPPRECONNECTION_MAIN_URL_USECOUNT_COL);
|
||
|
int subhostCol = cursor.getColumnIndex(TCPPRECONNECTION_SUBHOST_COL);
|
||
|
int subhostUseCountCol = cursor.getColumnIndex(TCPPRECONNECTION_SUBHOST_USECOUNT_COL);
|
||
|
int subhostWeightCol = cursor.getColumnIndex(TCPPRECONNECTION_SUBHOST_WEIGHT_COL);
|
||
|
|
||
|
do {
|
||
|
String subhostUrl = cursor.getString(subhostCol);
|
||
|
int subhostUseCount = cursor.getInt(subhostUseCountCol);
|
||
|
double subhostWeight = cursor.getDouble(subhostWeightCol);
|
||
|
String mainUrl = cursor.getString(mainUrlCol);
|
||
|
int mainUrlUseCount = cursor.getInt(mainUrlUseCountCol);
|
||
|
|
||
|
Subhost subhost = new Subhost(subhostUrl, subhostUseCount, subhostWeight);
|
||
|
subResourcesHistory.addSubHost(mainUrl, subhost);
|
||
|
subResourcesHistory.setUseCount(mainUrl, mainUrlUseCount);
|
||
|
} while (cursor.moveToNext());
|
||
|
|
||
|
subResourcesHistory.updateSubhostsToConnect();
|
||
|
}
|
||
|
} catch (IllegalStateException e) {
|
||
|
Log.e(LOGTAG, "IllegalStateException", e);
|
||
|
} finally {
|
||
|
if (cursor != null) cursor.close();
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|