1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.chromium.android_webview;
7 import android.content.ContentValues;
8 import android.content.Context;
9 import android.database.Cursor;
10 import android.database.sqlite.SQLiteDatabase;
11 import android.database.sqlite.SQLiteException;
12 import android.util.Log;
15 * This database is used to support WebView's setHttpAuthUsernamePassword and
16 * getHttpAuthUsernamePassword methods, and WebViewDatabase's clearHttpAuthUsernamePassword and
17 * hasHttpAuthUsernamePassword methods.
19 * While this class is intended to be used as a singleton, this property is not enforced in this
20 * layer, primarily for ease of testing. To line up with the classic implementation and behavior,
21 * there is no specific handling and reporting when SQL errors occur.
23 * Note on thread-safety: As per the classic implementation, most API functions have thread safety
24 * provided by the underlying SQLiteDatabase instance. The exception is database opening: this
25 * is handled in the dedicated background thread, which also provides a performance gain
26 * if triggered early on (e.g. as a side effect of CookieSyncManager.createInstance() call),
27 * sufficiently in advance of the first blocking usage of the API.
29 public class HttpAuthDatabase {
31 private static final String LOGTAG = "HttpAuthDatabase";
33 private static final int DATABASE_VERSION = 1;
35 private SQLiteDatabase mDatabase = null;
37 private static final String ID_COL = "_id";
39 private static final String[] ID_PROJECTION = new String[] {
43 // column id strings for "httpauth" table
44 private static final String HTTPAUTH_TABLE_NAME = "httpauth";
45 private static final String HTTPAUTH_HOST_COL = "host";
46 private static final String HTTPAUTH_REALM_COL = "realm";
47 private static final String HTTPAUTH_USERNAME_COL = "username";
48 private static final String HTTPAUTH_PASSWORD_COL = "password";
51 * Initially false until the background thread completes.
53 private boolean mInitialized = false;
55 private final Object mInitializedLock = new Object();
58 * Creates and returns an instance of HttpAuthDatabase for the named file, and kicks-off
59 * background initialization of that database.
61 * @param context the Context to use for opening the database
62 * @param databaseFile Name of the file to be initialized.
64 public static HttpAuthDatabase newInstance(final Context context, final String databaseFile) {
65 final HttpAuthDatabase httpAuthDatabase = new HttpAuthDatabase();
69 httpAuthDatabase.initOnBackgroundThread(context, databaseFile);
72 return httpAuthDatabase;
75 // Prevent instantiation. Callers should use newInstance().
76 private HttpAuthDatabase() {}
79 * Initializes the databases and notifies any callers waiting on waitForInit.
81 * @param context the Context to use for opening the database
82 * @param databaseFile Name of the file to be initialized.
84 private void initOnBackgroundThread(Context context, String databaseFile) {
85 synchronized (mInitializedLock) {
90 initDatabase(context, databaseFile);
92 // Thread done, notify.
94 mInitializedLock.notifyAll();
99 * Opens the database, and upgrades it if necessary.
101 * @param context the Context to use for opening the database
102 * @param databaseFile Name of the file to be initialized.
104 private void initDatabase(Context context, String databaseFile) {
106 mDatabase = context.openOrCreateDatabase(databaseFile, 0, null);
107 } catch (SQLiteException e) {
108 // try again by deleting the old db and create a new one
109 if (context.deleteDatabase(databaseFile)) {
110 mDatabase = context.openOrCreateDatabase(databaseFile, 0, null);
114 if (mDatabase == null) {
115 // Not much we can do to recover at this point
116 Log.e(LOGTAG, "Unable to open or create " + databaseFile);
120 if (mDatabase.getVersion() != DATABASE_VERSION) {
121 mDatabase.beginTransactionNonExclusive();
124 mDatabase.setTransactionSuccessful();
126 mDatabase.endTransaction();
131 private void createTable() {
132 mDatabase.execSQL("CREATE TABLE " + HTTPAUTH_TABLE_NAME
133 + " (" + ID_COL + " INTEGER PRIMARY KEY, "
134 + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL
135 + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, "
136 + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE ("
137 + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL
138 + ") ON CONFLICT REPLACE);");
140 mDatabase.setVersion(DATABASE_VERSION);
144 * Waits for the background initialization thread to complete and check the database creation
147 * @return true if the database was initialized, false otherwise
149 private boolean waitForInit() {
150 synchronized (mInitializedLock) {
151 while (!mInitialized) {
153 mInitializedLock.wait();
154 } catch (InterruptedException e) {
155 Log.e(LOGTAG, "Caught exception while checking initialization", e);
159 return mDatabase != null;
163 * Sets the HTTP authentication password. Tuple (HTTPAUTH_HOST_COL, HTTPAUTH_REALM_COL,
164 * HTTPAUTH_USERNAME_COL) is unique.
166 * @param host the host for the password
167 * @param realm the realm for the password
168 * @param username the username for the password.
169 * @param password the password
171 public void setHttpAuthUsernamePassword(String host, String realm, String username,
173 if (host == null || realm == null || !waitForInit()) {
177 final ContentValues c = new ContentValues();
178 c.put(HTTPAUTH_HOST_COL, host);
179 c.put(HTTPAUTH_REALM_COL, realm);
180 c.put(HTTPAUTH_USERNAME_COL, username);
181 c.put(HTTPAUTH_PASSWORD_COL, password);
182 mDatabase.insert(HTTPAUTH_TABLE_NAME, HTTPAUTH_HOST_COL, c);
186 * Retrieves the HTTP authentication username and password for a given host and realm pair. If
187 * there are multiple username/password combinations for a host/realm, only the first one will
190 * @param host the host the password applies to
191 * @param realm the realm the password applies to
192 * @return a String[] if found where String[0] is username (which can be null) and
193 * String[1] is password. Null is returned if it can't find anything.
195 public String[] getHttpAuthUsernamePassword(String host, String realm) {
196 if (host == null || realm == null || !waitForInit()) {
200 final String[] columns = new String[] {
201 HTTPAUTH_USERNAME_COL, HTTPAUTH_PASSWORD_COL
203 final String selection = "(" + HTTPAUTH_HOST_COL + " == ?) AND "
204 + "(" + HTTPAUTH_REALM_COL + " == ?)";
207 Cursor cursor = null;
209 cursor = mDatabase.query(HTTPAUTH_TABLE_NAME, columns, selection,
210 new String[] { host, realm }, null, null, null);
211 if (cursor.moveToFirst()) {
213 cursor.getString(cursor.getColumnIndex(HTTPAUTH_USERNAME_COL)),
214 cursor.getString(cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL)),
217 } catch (IllegalStateException e) {
218 Log.e(LOGTAG, "getHttpAuthUsernamePassword", e);
220 if (cursor != null) cursor.close();
226 * Determines if there are any HTTP authentication passwords saved.
228 * @return true if there are passwords saved
230 public boolean hasHttpAuthUsernamePassword() {
231 if (!waitForInit()) {
235 Cursor cursor = null;
238 cursor = mDatabase.query(HTTPAUTH_TABLE_NAME, ID_PROJECTION, null, null, null, null,
240 ret = cursor.moveToFirst();
241 } catch (IllegalStateException e) {
242 Log.e(LOGTAG, "hasEntries", e);
244 if (cursor != null) cursor.close();
250 * Clears the HTTP authentication password database.
252 public void clearHttpAuthUsernamePassword() {
253 if (!waitForInit()) {
256 mDatabase.delete(HTTPAUTH_TABLE_NAME, null, null);