Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / android_webview / java / src / org / chromium / android_webview / HttpAuthDatabase.java
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.
4
5 package org.chromium.android_webview;
6
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;
13
14 /**
15  * This database is used to support WebView's setHttpAuthUsernamePassword and
16  * getHttpAuthUsernamePassword methods, and WebViewDatabase's clearHttpAuthUsernamePassword and
17  * hasHttpAuthUsernamePassword methods.
18  *
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.
22  *
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.
28  */
29 public class HttpAuthDatabase {
30
31     private static final String LOGTAG = "HttpAuthDatabase";
32
33     private static final int DATABASE_VERSION = 1;
34
35     private SQLiteDatabase mDatabase = null;
36
37     private static final String ID_COL = "_id";
38
39     private static final String[] ID_PROJECTION = new String[] {
40         ID_COL
41     };
42
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";
49
50     /**
51      * Initially false until the background thread completes.
52      */
53     private boolean mInitialized = false;
54
55     private final Object mInitializedLock = new Object();
56
57     /**
58      * Creates and returns an instance of HttpAuthDatabase for the named file, and kicks-off
59      * background initialization of that database.
60      *
61      * @param context the Context to use for opening the database
62      * @param databaseFile Name of the file to be initialized.
63      */
64     public static HttpAuthDatabase newInstance(final Context context, final String databaseFile) {
65         final HttpAuthDatabase httpAuthDatabase = new HttpAuthDatabase();
66         new Thread() {
67             @Override
68             public void run() {
69                 httpAuthDatabase.initOnBackgroundThread(context, databaseFile);
70             }
71         }.start();
72         return httpAuthDatabase;
73     }
74
75     // Prevent instantiation. Callers should use newInstance().
76     private HttpAuthDatabase() {}
77
78     /**
79      * Initializes the databases and notifies any callers waiting on waitForInit.
80      *
81      * @param context the Context to use for opening the database
82      * @param databaseFile Name of the file to be initialized.
83      */
84     private void initOnBackgroundThread(Context context, String databaseFile) {
85         synchronized (mInitializedLock) {
86             if (mInitialized) {
87                 return;
88             }
89
90             initDatabase(context, databaseFile);
91
92             // Thread done, notify.
93             mInitialized = true;
94             mInitializedLock.notifyAll();
95         }
96     }
97
98     /**
99      * Opens the database, and upgrades it if necessary.
100      *
101      * @param context the Context to use for opening the database
102      * @param databaseFile Name of the file to be initialized.
103      */
104     private void initDatabase(Context context, String databaseFile) {
105         try {
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);
111             }
112         }
113
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);
117             return;
118         }
119
120         if (mDatabase.getVersion() != DATABASE_VERSION) {
121             mDatabase.beginTransactionNonExclusive();
122             try {
123                 createTable();
124                 mDatabase.setTransactionSuccessful();
125             } finally {
126                 mDatabase.endTransaction();
127             }
128         }
129     }
130
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);");
139
140         mDatabase.setVersion(DATABASE_VERSION);
141     }
142
143     /**
144      * Waits for the background initialization thread to complete and check the database creation
145      * status.
146      *
147      * @return true if the database was initialized, false otherwise
148      */
149     private boolean waitForInit() {
150         synchronized (mInitializedLock) {
151             while (!mInitialized) {
152                 try {
153                     mInitializedLock.wait();
154                 } catch (InterruptedException e) {
155                     Log.e(LOGTAG, "Caught exception while checking initialization", e);
156                 }
157             }
158         }
159         return mDatabase != null;
160     }
161
162     /**
163      * Sets the HTTP authentication password. Tuple (HTTPAUTH_HOST_COL, HTTPAUTH_REALM_COL,
164      * HTTPAUTH_USERNAME_COL) is unique.
165      *
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
170      */
171     public void setHttpAuthUsernamePassword(String host, String realm, String username,
172             String password) {
173         if (host == null || realm == null || !waitForInit()) {
174             return;
175         }
176
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);
183     }
184
185     /**
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
188      * be returned.
189      *
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.
194      */
195     public String[] getHttpAuthUsernamePassword(String host, String realm) {
196         if (host == null || realm == null || !waitForInit()) {
197             return null;
198         }
199
200         final String[] columns = new String[] {
201             HTTPAUTH_USERNAME_COL, HTTPAUTH_PASSWORD_COL
202         };
203         final String selection = "(" + HTTPAUTH_HOST_COL + " == ?) AND "
204                 + "(" + HTTPAUTH_REALM_COL + " == ?)";
205
206         String[] ret = null;
207         Cursor cursor = null;
208         try {
209             cursor = mDatabase.query(HTTPAUTH_TABLE_NAME, columns, selection,
210                     new String[] { host, realm }, null, null, null);
211             if (cursor.moveToFirst()) {
212                 ret = new String[] {
213                         cursor.getString(cursor.getColumnIndex(HTTPAUTH_USERNAME_COL)),
214                         cursor.getString(cursor.getColumnIndex(HTTPAUTH_PASSWORD_COL)),
215                 };
216             }
217         } catch (IllegalStateException e) {
218             Log.e(LOGTAG, "getHttpAuthUsernamePassword", e);
219         } finally {
220             if (cursor != null) cursor.close();
221         }
222         return ret;
223     }
224
225     /**
226      * Determines if there are any HTTP authentication passwords saved.
227      *
228      * @return true if there are passwords saved
229      */
230     public boolean hasHttpAuthUsernamePassword() {
231         if (!waitForInit()) {
232             return false;
233         }
234
235         Cursor cursor = null;
236         boolean ret = false;
237         try {
238             cursor = mDatabase.query(HTTPAUTH_TABLE_NAME, ID_PROJECTION, null, null, null, null,
239                     null);
240             ret = cursor.moveToFirst();
241         } catch (IllegalStateException e) {
242             Log.e(LOGTAG, "hasEntries", e);
243         } finally {
244             if (cursor != null) cursor.close();
245         }
246         return ret;
247     }
248
249     /**
250      * Clears the HTTP authentication password database.
251      */
252     public void clearHttpAuthUsernamePassword() {
253         if (!waitForInit()) {
254             return;
255         }
256         mDatabase.delete(HTTPAUTH_TABLE_NAME, null, null);
257     }
258 }