auto import from //depot/cupcake/@135843
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
new file mode 100644
index 0000000..1004e30
--- /dev/null
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -0,0 +1,967 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * 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.Set;
+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.SQLiteStatement;
+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 = 9;
+    // 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
+    private static final int CACHE_DATABASE_VERSION = 1;
+
+    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 static final String mTableNames[] = {
+        "cookies", "password", "formurl", "formdata", "httpauth"
+    };
+
+    // 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;
+
+    // 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_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";
+
+    // 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";
+
+    // 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 mCacheMimeTypeColIndex;
+    private static int mCacheEncodingColIndex;
+    private static int mCacheHttpStatusColIndex;
+    private static int mCacheLocationColIndex;
+    private static int mCacheContentLengthColIndex;
+
+    private static int mCacheTransactionRefcount;
+
+    private WebViewDatabase() {
+        // Singleton only, use getInstance()
+    }
+
+    public static synchronized WebViewDatabase getInstance(Context context) {
+        if (mInstance == null) {
+            mInstance = new WebViewDatabase();
+            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);
+            }
+
+            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 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);
+                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);
+            }
+        }
+
+        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;
+        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_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 + ", "
+                    + HTTPAUTH_USERNAME_COL + ") ON CONFLICT REPLACE);");
+        }
+        // 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_MIMETYPE_COL + " TEXT, " + CACHE_ENCODING_COL
+                    + " TEXT," + CACHE_HTTP_STATUS_COL + " INTEGER, "
+                    + CACHE_LOCATION_COL + " TEXT, " + CACHE_CONTENTLENGTH_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 = mDatabase.query(mTableNames[tableId], ID_PROJECTION,
+                null, null, null, null, null);
+        boolean ret = cursor.moveToFirst() == true;
+        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 = 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());
+            }
+            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);
+            mDatabase.insert(mTableNames[TABLE_COOKIES_ID], null, cookieVal);
+        }
+    }
+
+    /**
+     * 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, can only be called from WebCoreThread
+    //
+
+    boolean startCacheTransaction() {
+        if (++mCacheTransactionRefcount == 1) {
+            mCacheDatabase.beginTransaction();
+            return true;
+        }
+        return false;
+    }
+
+    boolean endCacheTransaction() {
+        if (--mCacheTransactionRefcount == 0) {
+            try {
+                mCacheDatabase.setTransactionSuccessful();
+            } finally {
+                mCacheDatabase.endTransaction();
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 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 = mCacheDatabase.rawQuery("SELECT filepath, lastmodify, etag, expires, "
+                    + "mimetype, encoding, httpstatus, location, contentlength "
+                    + "FROM cache WHERE url = ?",
+                new String[] { url });
+
+        try {
+            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.mimeType = cursor.getString(4);
+                ret.encoding = cursor.getString(5);
+                ret.httpStatusCode = cursor.getInt(6);
+                ret.location = cursor.getString(7);
+                ret.contentLength = cursor.getLong(8);
+                return ret;
+            }
+        } 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 });
+    }
+
+    /**
+     * 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(mCacheMimeTypeColIndex, c.mimeType);
+        mCacheInserter.bind(mCacheEncodingColIndex, c.encoding);
+        mCacheInserter.bind(mCacheHttpStatusColIndex, c.httpStatusCode);
+        mCacheInserter.bind(mCacheLocationColIndex, c.location);
+        mCacheInserter.bind(mCacheContentLengthColIndex, c.contentLength);
+        mCacheInserter.execute();
+    }
+
+    /**
+     * Clear cache database
+     */
+    void clearCache() {
+        if (mCacheDatabase == null) {
+            return;
+        }
+
+        mCacheDatabase.delete("cache", null, null);
+    }
+
+    boolean hasCache() {
+        if (mCacheDatabase == null) {
+            return false;
+        }
+
+        Cursor cursor = mCacheDatabase.query("cache", ID_PROJECTION,
+                null, null, null, null, null);
+        boolean ret = cursor.moveToFirst() == true;
+        cursor.close();
+        return ret;
+    }
+
+    long getCacheTotalSize() {
+        long size = 0;
+        Cursor cursor = mCacheDatabase.rawQuery(
+                "SELECT SUM(contentlength) as sum FROM cache", null);
+        if (cursor.moveToFirst()) {
+            size = cursor.getLong(0);
+        }
+        cursor.close();
+        return size;
+    }
+
+    ArrayList<String> trimCache(long amount) {
+        ArrayList<String> pathList = new ArrayList<String>(100);
+        Cursor cursor = mCacheDatabase.rawQuery(
+                "SELECT contentlength, filepath FROM cache ORDER BY expires ASC",
+                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 = 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();
+            }
+            statement.close();
+        }
+        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);
+            mDatabase.insert(mTableNames[TABLE_PASSWORD_ID], PASSWORD_HOST_COL,
+                    c);
+        }
+    }
+
+    /**
+     * 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 = 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));
+            }
+            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);
+            mDatabase.insert(mTableNames[TABLE_HTTPAUTH_ID], HTTPAUTH_HOST_COL,
+                    c);
+        }
+    }
+
+    /**
+     * 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 = 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));
+            }
+            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 = 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);
+            }
+            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());
+                    mDatabase.insert(mTableNames[TABLE_FORMDATA_ID], null, map);
+                }
+            }
+        }
+    }
+
+    /**
+     * 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 = 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 = 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());
+                }
+                dataCursor.close();
+            }
+            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);
+        }
+    }
+}