1 // Copyright (c) 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.xwalk.core;
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 the methods of
16 * setHttpAuthUsernamePassword() and getHttpAuthUsernamePassword()
17 * clearHttpAuthUsernamePassword() and hasHttpAuthUsernamePassword().
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 class HttpAuthDatabase {
31 private static final String LOGTAG = HttpAuthDatabase.class.getName();
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;
56 * Create an instance of HttpAuthDatabase for the named file, and kick-off background
57 * initialization of that database.
59 * @param context the Context to use for opening the database
60 * @param databaseFile Name of the file to be initialized.
62 public HttpAuthDatabase(final Context context, final String databaseFile) {
66 initOnBackgroundThread(context, databaseFile);
72 * Initializes the databases and notifies any callers waiting on waitForInit.
74 * @param context the Context to use for opening the database
75 * @param databaseFile Name of the file to be initialized.
77 private synchronized void initOnBackgroundThread(Context context, String databaseFile) {
82 initDatabase(context, databaseFile);
84 // Thread done, notify.
90 * Opens the database, and upgrades it if necessary.
92 * @param context the Context to use for opening the database
93 * @param databaseFile Name of the file to be initialized.
95 private void initDatabase(Context context, String databaseFile) {
97 mDatabase = context.openOrCreateDatabase(databaseFile, 0, null);
98 } catch (SQLiteException e) {
99 // try again by deleting the old db and create a new one
100 if (context.deleteDatabase(databaseFile)) {
101 mDatabase = context.openOrCreateDatabase(databaseFile, 0, null);
105 if (mDatabase == null) {
106 // Not much we can do to recover at this point
107 Log.e(LOGTAG, "Unable to open or create " + databaseFile);
111 if (mDatabase.getVersion() != DATABASE_VERSION) {
112 mDatabase.beginTransactionNonExclusive();
115 mDatabase.setTransactionSuccessful();
117 mDatabase.endTransaction();
122 private void createTable() {
123 mDatabase.execSQL("CREATE TABLE " + HTTPAUTH_TABLE_NAME
124 + " (" + ID_COL + " INTEGER PRIMARY KEY, "
125 + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL
126 + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, "
127 + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE ("
128 + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL
129 + ") ON CONFLICT REPLACE);");
131 mDatabase.setVersion(DATABASE_VERSION);
135 * Waits for the background initialization thread to complete and check the database creation
138 * @return true if the database was initialized, false otherwise
140 private boolean waitForInit() {
141 synchronized (this) {
142 while (!mInitialized) {
145 } catch (InterruptedException e) {
146 Log.e(LOGTAG, "Caught exception while checking initialization", e);
150 return mDatabase != null;
154 * Sets the HTTP authentication password. Tuple (HTTPAUTH_HOST_COL, HTTPAUTH_REALM_COL,
155 * HTTPAUTH_USERNAME_COL) is unique.
157 * @param host the host for the password
158 * @param realm the realm for the password
159 * @param username the username for the password.
160 * @param password the password
162 public void setHttpAuthUsernamePassword(String host, String realm, String username,
164 if (host == null || realm == null || !waitForInit()) {
168 final ContentValues c = new ContentValues();
169 c.put(HTTPAUTH_HOST_COL, host);
170 c.put(HTTPAUTH_REALM_COL, realm);
171 c.put(HTTPAUTH_USERNAME_COL, username);
172 c.put(HTTPAUTH_PASSWORD_COL, password);
173 mDatabase.insert(HTTPAUTH_TABLE_NAME, HTTPAUTH_HOST_COL, c);
177 * Retrieves the HTTP authentication username and password for a given host and realm pair. If
178 * there are multiple username/password combinations for a host/realm, only the first one will
181 * @param host the host the password applies to
182 * @param realm the realm the password applies to
183 * @return a String[] if found where String[0] is username (which can be null) and
184 * String[1] is password. Null is returned if it can't find anything.
186 public String[] getHttpAuthUsernamePassword(String host, String realm) {
187 if (host == null || realm == null || !waitForInit()){
191 final String[] columns = new String[] {
192 HTTPAUTH_USERNAME_COL, HTTPAUTH_PASSWORD_COL
194 final String selection = "(" + HTTPAUTH_HOST_COL + " == ?) AND " +
195 "(" + HTTPAUTH_REALM_COL + " == ?)";
198 Cursor cursor = null;
200 cursor = mDatabase.query(HTTPAUTH_TABLE_NAME, columns, selection,
201 new String[] { host, realm }, null, null, null);
202 if (cursor.moveToFirst()) {
204 cursor.getString(cursor.getColumnIndex(HTTPAUTH_USERNAME_COL)),
205 cursor.getString(cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL)),
208 } catch (IllegalStateException e) {
209 Log.e(LOGTAG, "getHttpAuthUsernamePassword", e);
211 if (cursor != null) cursor.close();
217 * Determines if there are any HTTP authentication passwords saved.
219 * @return true if there are passwords saved
221 public boolean hasHttpAuthUsernamePassword() {
222 if (!waitForInit()) {
226 Cursor cursor = null;
229 cursor = mDatabase.query(HTTPAUTH_TABLE_NAME, ID_PROJECTION, null, null, null, null,
231 ret = cursor.moveToFirst();
232 } catch (IllegalStateException e) {
233 Log.e(LOGTAG, "hasEntries", e);
235 if (cursor != null) cursor.close();
241 * Clears the HTTP authentication password database.
243 public void clearHttpAuthUsernamePassword() {
244 if (!waitForInit()) {
247 mDatabase.delete(HTTPAUTH_TABLE_NAME, null, null);