Create WebViewDatabaseClassic from WebViewDatabase.

WebViewDatabase has a getInstance() method, so similarly
to WebStorage, WebIconDatabase etc we refactor it into a
proxy class, and move the current implementation into
WebViewDatabaseClassic.

Also clean up some JavaDoc in touched files.

Bug: 6234236
Change-Id: I71cbd8f78e60f396e96e8546073ad634797cce15
diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java
index 6c35f19..9d10d67 100644
--- a/core/java/android/webkit/WebViewDatabase.java
+++ b/core/java/android/webkit/WebViewDatabase.java
@@ -16,611 +16,79 @@
 
 package android.webkit;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-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.SQLiteException;
-import android.database.sqlite.SQLiteStatement;
-import android.util.Log;
 
+/**
+ * This class allows developers to determine whether any WebView used in the
+ * application has stored any of the following types of browsing data and
+ * to clear any such stored data for all WebViews in the application.
+ * <ul>
+ *  <li>Username/password pairs entered into web forms</li>
+ *  <li>HTTP authentication username/password pairs</li>
+ *  <li>Data entered into text fields (e.g. for autocomplete suggestions)</li>
+ * </ul>
+ */
 public class WebViewDatabase {
-    private static final String DATABASE_FILE = "webview.db";
-    private static final String CACHE_DATABASE_FILE = "webviewCache.db";
-
-    // log tag
+    // TODO: deprecate/hide this.
     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 Drop cookies and cache now managed by the chromium stack,
-    //          and update the form data table to use the new format
-    //          implemented for b/5265606.
-
-    private static WebViewDatabase mInstance = null;
-
-    private static SQLiteDatabase mDatabase = null;
-
-    // synchronize locks
-    private final Object mPasswordLock = new Object();
-    private final Object mFormLock = new Object();
-    private final Object mHttpAuthLock = new Object();
-
-    private static final String mTableNames[] = {
-        "password", "formurl", "formdata", "httpauth"
-    };
-
-    // Table ids (they are index to mTableNames)
-    private static final int TABLE_PASSWORD_ID = 0;
-    private static final int TABLE_FORMURL_ID = 1;
-    private static final int TABLE_FORMDATA_ID = 2;
-    private static final int TABLE_HTTPAUTH_ID = 3;
-
-    // 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 "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";
-
-    // Initially true until the background thread completes.
-    private boolean mInitialized = false;
-
-    private WebViewDatabase(final Context context) {
-        new Thread() {
-            @Override
-            public void run() {
-                init(context);
-            }
-        }.start();
-
-        // Singleton only, use getInstance()
+    /**
+     * @hide Only for use by WebViewProvider implementations.
+     */
+    protected WebViewDatabase() {
     }
 
     public static synchronized WebViewDatabase getInstance(Context context) {
-        if (mInstance == null) {
-            mInstance = new WebViewDatabase(context);
-        }
-        return mInstance;
-    }
-
-    private synchronized void init(Context context) {
-        if (mInitialized) {
-            return;
-        }
-
-        initDatabase(context);
-	// Before using the Chromium HTTP stack, we stored the WebKit cache in
-	// our own DB. Clean up the DB file if it's still around.
-        context.deleteDatabase(CACHE_DATABASE_FILE);
-
-        // Thread done, notify.
-        mInitialized = true;
-        notify();
-    }
-
-    private void initDatabase(Context context) {
-        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) {
-            mInitialized = true;
-            notify();
-            return;
-        }
-
-        if (mDatabase.getVersion() != DATABASE_VERSION) {
-            mDatabase.beginTransactionNonExclusive();
-            try {
-                upgradeDatabase();
-                mDatabase.setTransactionSuccessful();
-            } finally {
-                mDatabase.endTransaction();
-            }
-        }
-    }
-
-    private static void upgradeDatabase() {
-        upgradeDatabaseToV10();
-        upgradeDatabaseFromV10ToV11();
-        // Add future database upgrade functions here, one version at a
-        // time.
-        mDatabase.setVersion(DATABASE_VERSION);
-    }
-
-    private static void upgradeDatabaseFromV10ToV11() {
-        int oldVersion = mDatabase.getVersion();
-
-        if (oldVersion >= 11) {
-            // Nothing to do.
-            return;
-        }
-
-        // Clear out old java stack cookies - this data is now stored in
-        // a separate database managed by the Chrome stack.
-        mDatabase.execSQL("DROP TABLE IF EXISTS cookies");
-
-        // Likewise for the old cache table.
-        mDatabase.execSQL("DROP TABLE IF EXISTS cache");
-
-        // Update form autocomplete  URLs to match new ICS formatting.
-        Cursor c = mDatabase.query(mTableNames[TABLE_FORMURL_ID], null, null,
-                null, null, null, null);
-        while (c.moveToNext()) {
-            String urlId = Long.toString(c.getLong(c.getColumnIndex(ID_COL)));
-            String url = c.getString(c.getColumnIndex(FORMURL_URL_COL));
-            ContentValues cv = new ContentValues(1);
-            cv.put(FORMURL_URL_COL, WebTextView.urlForAutoCompleteData(url));
-            mDatabase.update(mTableNames[TABLE_FORMURL_ID], cv, ID_COL + "=?",
-                    new String[] { urlId });
-        }
-        c.close();
-    }
-
-    private static void upgradeDatabaseToV10() {
-        int oldVersion = mDatabase.getVersion();
-
-        if (oldVersion >= 10) {
-            // Nothing to do.
-            return;
-        }
-
-        if (oldVersion != 0) {
-            Log.i(LOGTAG, "Upgrading database from version "
-                    + oldVersion + " to "
-                    + DATABASE_VERSION + ", which will destroy old data");
-        }
-
-        if (9 == oldVersion) {
-            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;
-        }
-
-        mDatabase.execSQL("DROP TABLE IF EXISTS cookies");
-        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]);
-
-        // 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);");
-        // 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);");
-    }
-
-    // Wait for the background initialization thread to complete and check the
-    // database creation status.
-    private boolean checkInitialized() {
-        synchronized (this) {
-            while (!mInitialized) {
-                try {
-                    wait();
-                } catch (InterruptedException e) {
-                    Log.e(LOGTAG, "Caught exception while checking " +
-                                  "initialization");
-                    Log.e(LOGTAG, Log.getStackTraceString(e));
-                }
-            }
-        }
-        return mDatabase != null;
-    }
-
-    private boolean hasEntries(int tableId) {
-        if (!checkInitialized()) {
-            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;
-    }
-
-    //
-    // 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 || !checkInitialized()) {
-            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);
-        }
+        return WebViewFactory.getProvider().getWebViewDatabase(context);
     }
 
     /**
-     * Retrieve the username and password for a given host
+     * Gets whether there are any username/password combinations
+     * from web pages saved.
      *
-     * @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 || !checkInitialized()) {
-            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
+     * @return true if there are any username/passwords used in web
+     *         forms saved
      */
     public boolean hasUsernamePassword() {
-        synchronized (mPasswordLock) {
-            return hasEntries(TABLE_PASSWORD_ID);
-        }
+        throw new MustOverrideException();
     }
 
     /**
-     * Clear password database
+     * Clears any username/password combinations saved from web forms.
      */
     public void clearUsernamePassword() {
-        if (!checkInitialized()) {
-            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 || !checkInitialized()) {
-            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);
-        }
+        throw new MustOverrideException();
     }
 
     /**
-     * Retrieve the HTTP authentication username and password for a given
-     * host+realm pair
+     * Gets whether there are any HTTP authentication username/password combinations saved.
      *
-     * @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 || !checkInitialized()){
-            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
+     * @return true if there are any HTTP authentication username/passwords saved
      */
     public boolean hasHttpAuthUsernamePassword() {
-        synchronized (mHttpAuthLock) {
-            return hasEntries(TABLE_HTTPAUTH_ID);
-        }
+        throw new MustOverrideException();
     }
 
     /**
-     * Clear HTTP authentication password database
+     * Clears any HTTP authentication username/passwords that are saved.
      */
     public void clearHttpAuthUsernamePassword() {
-        if (!checkInitialized()) {
-            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 || !checkInitialized()) {
-            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());
-                    mDatabase.insert(mTableNames[TABLE_FORMDATA_ID], null, map);
-                }
-            }
-        }
+        throw new MustOverrideException();
     }
 
     /**
-     * Get all the values for a form entry with "name" in a given site
+     * Gets whether there is any previously-entered form data saved.
      *
-     * @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 || !checkInitialized()) {
-            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);
-                while (cursor.moveToNext()) {
-                    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
+     * @return true if there is form data saved
      */
     public boolean hasFormData() {
-        synchronized (mFormLock) {
-            return hasEntries(TABLE_FORMURL_ID);
-        }
+        throw new MustOverrideException();
     }
 
     /**
-     * Clear form database
+     * Clears any stored previously-entered form data.
      */
     public void clearFormData() {
-        if (!checkInitialized()) {
-            return;
-        }
-
-        synchronized (mFormLock) {
-            mDatabase.delete(mTableNames[TABLE_FORMURL_ID], null, null);
-            mDatabase.delete(mTableNames[TABLE_FORMDATA_ID], null, null);
-        }
+        throw new MustOverrideException();
     }
 }