summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/Activity.java30
-rw-r--r--core/java/android/app/ActivityManager.java8
-rw-r--r--core/java/android/app/ActivityManagerNative.java23
-rw-r--r--core/java/android/app/IActivityManager.java2
-rw-r--r--core/java/android/webkit/BrowserFrame.java11
-rw-r--r--core/java/android/webkit/WebSettings.java55
-rw-r--r--core/java/android/webkit/WebViewClassic.java12
-rw-r--r--core/java/android/webkit/WebViewDatabase.java596
-rw-r--r--core/java/android/webkit/WebViewDatabaseClassic.java624
-rw-r--r--core/java/android/webkit/WebViewFactoryProvider.java19
-rw-r--r--core/java/android/widget/RemoteViews.java4
11 files changed, 782 insertions, 602 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 35bc7ff..4add7f4 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4093,6 +4093,36 @@ public class Activity extends ContextThemeWrapper
}
/**
+ * Finish this activity as well as all activities immediately below it
+ * in the current task that have the same affinity. This is typically
+ * used when an application can be launched on to another task (such as
+ * from an ACTION_VIEW of a content type it understands) and the user
+ * has used the up navigation to switch out of the current task and in
+ * to its own task. In this case, if the user has navigated down into
+ * any other activities of the second application, all of those should
+ * be removed from the original task as part of the task switch.
+ *
+ * <p>Note that this finish does <em>not</em> allow you to deliver results
+ * to the previous activity, and an exception will be thrown if you are trying
+ * to do so.</p>
+ */
+ public void finishAffinity() {
+ if (mParent != null) {
+ throw new IllegalStateException("Can not be called from an embedded activity");
+ }
+ if (mResultCode != RESULT_CANCELED || mResultData != null) {
+ throw new IllegalStateException("Can not be called to deliver a result");
+ }
+ try {
+ if (ActivityManagerNative.getDefault().finishActivityAffinity(mToken)) {
+ mFinished = true;
+ }
+ } catch (RemoteException e) {
+ // Empty
+ }
+ }
+
+ /**
* This is called when a child activity of this one calls its
* {@link #finish} method. The default implementation simply calls
* finish() on this activity (the parent), finishing the entire group.
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 7dce2d3..7746ca9 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1687,6 +1687,8 @@ public class ActivityManager {
return DisplayMetrics.DENSITY_MEDIUM;
case DisplayMetrics.DENSITY_MEDIUM:
return DisplayMetrics.DENSITY_HIGH;
+ case DisplayMetrics.DENSITY_TV:
+ return DisplayMetrics.DENSITY_XHIGH;
case DisplayMetrics.DENSITY_HIGH:
return DisplayMetrics.DENSITY_XHIGH;
case DisplayMetrics.DENSITY_XHIGH:
@@ -1696,7 +1698,7 @@ public class ActivityManager {
default:
// The density is some abnormal value. Return some other
// abnormal value that is a reasonable scaling of it.
- return (int)(density*1.5f);
+ return (int)((density*1.5f)+.5f);
}
}
@@ -1723,6 +1725,8 @@ public class ActivityManager {
return (size * DisplayMetrics.DENSITY_MEDIUM) / DisplayMetrics.DENSITY_LOW;
case DisplayMetrics.DENSITY_MEDIUM:
return (size * DisplayMetrics.DENSITY_HIGH) / DisplayMetrics.DENSITY_MEDIUM;
+ case DisplayMetrics.DENSITY_TV:
+ return (size * DisplayMetrics.DENSITY_XHIGH) / DisplayMetrics.DENSITY_HIGH;
case DisplayMetrics.DENSITY_HIGH:
return (size * DisplayMetrics.DENSITY_XHIGH) / DisplayMetrics.DENSITY_HIGH;
case DisplayMetrics.DENSITY_XHIGH:
@@ -1732,7 +1736,7 @@ public class ActivityManager {
default:
// The density is some abnormal value. Return some other
// abnormal value that is a reasonable scaling of it.
- return (int)(size*1.5f);
+ return (int)((size*1.5f) + .5f);
}
}
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 7e1589f..2f2918d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -218,7 +218,7 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
reply.writeInt(result ? 1 : 0);
return true;
}
-
+
case FINISH_ACTIVITY_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -243,6 +243,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case FINISH_ACTIVITY_AFFINITY_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IBinder token = data.readStrongBinder();
+ boolean res = finishActivityAffinity(token);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
+
case WILL_ACTIVITY_BE_VISIBLE_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
@@ -1866,6 +1875,18 @@ class ActivityManagerProxy implements IActivityManager
data.recycle();
reply.recycle();
}
+ public boolean finishActivityAffinity(IBinder token) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(token);
+ mRemote.transact(FINISH_ACTIVITY_AFFINITY_TRANSACTION, data, reply, 0);
+ reply.readException();
+ boolean res = reply.readInt() != 0;
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
public boolean willActivityBeVisible(IBinder token) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 3fc2280..a2c7fa4 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -72,6 +72,7 @@ public interface IActivityManager extends IInterface {
public boolean finishActivity(IBinder token, int code, Intent data)
throws RemoteException;
public void finishSubActivity(IBinder token, String resultWho, int requestCode) throws RemoteException;
+ public boolean finishActivityAffinity(IBinder token) throws RemoteException;
public boolean willActivityBeVisible(IBinder token) throws RemoteException;
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter,
@@ -590,4 +591,5 @@ public interface IActivityManager extends IInterface {
int TARGET_TASK_AFFINITY_MATCHES_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+145;
int NAVIGATE_UP_TO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+146;
int SET_LOCK_SCREEN_SHOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+147;
+ int FINISH_ACTIVITY_AFFINITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+148;
}
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 7b6b54c..c169de4 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -72,7 +72,7 @@ class BrowserFrame extends Handler {
private final CallbackProxy mCallbackProxy;
private final WebSettingsClassic mSettings;
private final Context mContext;
- private final WebViewDatabase mDatabase;
+ private final WebViewDatabaseClassic mDatabase;
private final WebViewCore mWebViewCore;
/* package */ boolean mLoadInitFromJava;
private int mLoadType;
@@ -241,7 +241,7 @@ class BrowserFrame extends Handler {
mSettings = settings;
mContext = context;
mCallbackProxy = proxy;
- mDatabase = WebViewDatabase.getInstance(appContext);
+ mDatabase = WebViewDatabaseClassic.getInstance(appContext);
mWebViewCore = w;
mSearchBox = new SearchBoxImpl(mWebViewCore, mCallbackProxy);
@@ -496,8 +496,8 @@ class BrowserFrame extends Handler {
if (item != null) {
WebAddress uri = new WebAddress(item.getUrl());
String schemePlusHost = uri.getScheme() + uri.getHost();
- String[] up =
- mDatabase.getUsernamePassword(schemePlusHost);
+ String[] up = mDatabase.getUsernamePassword(
+ schemePlusHost);
if (up != null && up[0] != null) {
setUsernamePassword(up[0], up[1]);
}
@@ -809,8 +809,7 @@ class BrowserFrame extends Handler {
// non-null username implies that user has
// chosen to save password, so update the
// recorded password
- mDatabase.setUsernamePassword(
- schemePlusHost, username, password);
+ mDatabase.setUsernamePassword(schemePlusHost, username, password);
}
} else {
// CallbackProxy will handle creating the resume
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 901372b..fa3cb20 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -848,25 +848,37 @@ public abstract class WebSettings {
}
/**
- * Configures scripting (such as XmlHttpRequest) access from file scheme URLs
- * to any origin. Note, calling this method with a true argument value also
- * implies calling setAllowFileAccessFromFileURLs with a true. The default
- * value is false for API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
- * and higher and true otherwise.
+ * Sets whether JavaScript running in the context of a file scheme URL
+ * should be allowed to access content from any origin. This includes
+ * access to content from other file scheme URLs. See
+ * {@link #setAllowFileAccessFromFileURLs}. To enable the most restrictive,
+ * and therefore secure policy, this setting should be disabled.
+ * <p>
+ * The default value is true for API level
+ * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
+ * and false for API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
+ * and above.
*
- * @param flag true if the WebView should allow scripting access from file
- * scheme URLs to any origin
+ * @param flag whether JavaScript running in the context of a file scheme
+ * URL should be allowed to access content from any origin
*/
public abstract void setAllowUniversalAccessFromFileURLs(boolean flag);
/**
- * Configures scripting (such as XmlHttpRequest) access from file scheme URLs
- * to file origin. The default value is false for API level
- * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} and higher and true
- * otherwise.
+ * Sets whether JavaScript running in the context of a file scheme URL
+ * should be allowed to access content from other file scheme URLs. To
+ * enable the most restrictive, and therefore secure policy, this setting
+ * should be disabled. Note that the value of this setting is ignored if
+ * the value of {@link #getAllowUniversalAccessFromFileURLs} is true.
+ * <p>
+ * The default value is true for API level
+ * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH_MR1} and below,
+ * and false for API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN}
+ * and above.
*
- * @param flag true if the WebView should allow scripting access from file
- * scheme URLs to file origin
+ * @param flag whether JavaScript running in the context of a file scheme
+ * URL should be allowed to access content from other file
+ * scheme URLs
*/
public abstract void setAllowFileAccessFromFileURLs(boolean flag);
@@ -1028,21 +1040,22 @@ public abstract class WebSettings {
}
/**
- * Gets whether scripting access {see @setAllowUniversalAccessFromFileURLs} from
- * file URLs to any origin is enabled.
+ * Gets whether JavaScript running in the context of a file scheme URL can
+ * access content from any origin. This includes access to content from
+ * other file scheme URLs.
*
- * @return true if the WebView allows scripting access from file scheme requests
- * to any origin
+ * @return whether JavaScript running in the context of a file scheme URL
+ * can access content from any origin
* @see #setAllowUniversalAccessFromFileURLs
*/
public abstract boolean getAllowUniversalAccessFromFileURLs();
/**
- * Gets whether scripting access {see @setAllowFileAccessFromFileURLs} from file
- * URLs to file origin is enabled.
+ * Gets whether JavaScript running in the context of a file scheme URL can
+ * access content from other file scheme URLs.
*
- * @return true if the WebView allows scripting access from file scheme requests
- * to file origin
+ * @return whether JavaScript running in the context of a file scheme URL
+ * can access content from other file scheme URLs
* @see #setAllowFileAccessFromFileURLs
*/
public abstract boolean getAllowFileAccessFromFileURLs();
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 9cf0d54..ca17d31 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -701,7 +701,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
// A final CallbackProxy shared by WebViewCore and BrowserFrame.
private CallbackProxy mCallbackProxy;
- private WebViewDatabase mDatabase;
+ private WebViewDatabaseClassic mDatabase;
// SSL certificate for the main top-level page (if secure)
private SslCertificate mCertificate;
@@ -1230,7 +1230,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
mViewManager = new ViewManager(this);
L10nUtils.setApplicationContext(context.getApplicationContext());
mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javaScriptInterfaces);
- mDatabase = WebViewDatabase.getInstance(context);
+ mDatabase = WebViewDatabaseClassic.getInstance(context);
mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel
mZoomManager = new ZoomManager(this, mCallbackProxy);
@@ -1294,6 +1294,11 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
public WebStorage getWebStorage() {
return WebStorageClassic.getInstance();
}
+
+ @Override
+ public WebViewDatabase getWebViewDatabase(Context context) {
+ return WebViewDatabaseClassic.getInstance(context);
+ }
}
private void onHandleUiEvent(MotionEvent event, int eventType, int flags) {
@@ -7192,8 +7197,7 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
break;
}
case NEVER_REMEMBER_PASSWORD: {
- mDatabase.setUsernamePassword(
- msg.getData().getString("host"), null, null);
+ mDatabase.setUsernamePassword(msg.getData().getString("host"), null, null);
((Message) msg.obj).sendToTarget();
break;
}
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()
- }
-
- 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
+ * @hide Only for use by WebViewProvider implementations.
*/
- 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);
- }
+ protected WebViewDatabase() {
}
- /**
- * 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 || !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;
- }
+ public static synchronized WebViewDatabase getInstance(Context context) {
+ return WebViewFactory.getProvider().getWebViewDatabase(context);
}
/**
- * Find out if there are any passwords saved.
+ * Gets whether there are any username/password combinations
+ * from web pages 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();
}
}
diff --git a/core/java/android/webkit/WebViewDatabaseClassic.java b/core/java/android/webkit/WebViewDatabaseClassic.java
new file mode 100644
index 0000000..9b1d4cb
--- /dev/null
+++ b/core/java/android/webkit/WebViewDatabaseClassic.java
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2012 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.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;
+
+final class WebViewDatabaseClassic extends WebViewDatabase {
+ private static final String LOGTAG = "WebViewDatabaseClassic";
+ private static final String DATABASE_FILE = "webview.db";
+ private static final String CACHE_DATABASE_FILE = "webviewCache.db";
+
+ 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 WebViewDatabaseClassic sInstance = null;
+
+ private static SQLiteDatabase sDatabase = 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;
+
+ WebViewDatabaseClassic(final Context context) {
+ new Thread() {
+ @Override
+ public void run() {
+ init(context);
+ }
+ }.start();
+
+ // Singleton only, use getInstance()
+ }
+
+ public static synchronized WebViewDatabaseClassic getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new WebViewDatabaseClassic(context);
+ }
+ return sInstance;
+ }
+
+ 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 {
+ sDatabase = 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)) {
+ sDatabase = context.openOrCreateDatabase(DATABASE_FILE, 0,
+ null);
+ }
+ }
+
+ // sDatabase should not be null,
+ // the only case is RequestAPI test has problem to create db
+ if (sDatabase == null) {
+ mInitialized = true;
+ notify();
+ return;
+ }
+
+ if (sDatabase.getVersion() != DATABASE_VERSION) {
+ sDatabase.beginTransactionNonExclusive();
+ try {
+ upgradeDatabase();
+ sDatabase.setTransactionSuccessful();
+ } finally {
+ sDatabase.endTransaction();
+ }
+ }
+ }
+
+ private static void upgradeDatabase() {
+ upgradeDatabaseToV10();
+ upgradeDatabaseFromV10ToV11();
+ // Add future database upgrade functions here, one version at a
+ // time.
+ sDatabase.setVersion(DATABASE_VERSION);
+ }
+
+ private static void upgradeDatabaseFromV10ToV11() {
+ int oldVersion = sDatabase.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.
+ sDatabase.execSQL("DROP TABLE IF EXISTS cookies");
+
+ // Likewise for the old cache table.
+ sDatabase.execSQL("DROP TABLE IF EXISTS cache");
+
+ // Update form autocomplete URLs to match new ICS formatting.
+ Cursor c = sDatabase.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));
+ sDatabase.update(mTableNames[TABLE_FORMURL_ID], cv, ID_COL + "=?",
+ new String[] { urlId });
+ }
+ c.close();
+ }
+
+ private static void upgradeDatabaseToV10() {
+ int oldVersion = sDatabase.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) {
+ sDatabase.execSQL("DROP TABLE IF EXISTS "
+ + mTableNames[TABLE_HTTPAUTH_ID]);
+ sDatabase.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;
+ }
+
+ sDatabase.execSQL("DROP TABLE IF EXISTS cookies");
+ sDatabase.execSQL("DROP TABLE IF EXISTS cache");
+ sDatabase.execSQL("DROP TABLE IF EXISTS "
+ + mTableNames[TABLE_FORMURL_ID]);
+ sDatabase.execSQL("DROP TABLE IF EXISTS "
+ + mTableNames[TABLE_FORMDATA_ID]);
+ sDatabase.execSQL("DROP TABLE IF EXISTS "
+ + mTableNames[TABLE_HTTPAUTH_ID]);
+ sDatabase.execSQL("DROP TABLE IF EXISTS "
+ + mTableNames[TABLE_PASSWORD_ID]);
+
+ // formurl
+ sDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMURL_ID]
+ + " (" + ID_COL + " INTEGER PRIMARY KEY, " + FORMURL_URL_COL
+ + " TEXT" + ");");
+
+ // formdata
+ sDatabase.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
+ sDatabase.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
+ sDatabase.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 sDatabase != null;
+ }
+
+ private boolean hasEntries(int tableId) {
+ if (!checkInitialized()) {
+ return false;
+ }
+
+ Cursor cursor = null;
+ boolean ret = false;
+ try {
+ cursor = sDatabase.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);
+ sDatabase.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 || !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 = sDatabase.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;
+ }
+ }
+
+ /**
+ * @see WebViewDatabase#hasUsernamePassword
+ */
+ @Override
+ public boolean hasUsernamePassword() {
+ synchronized (mPasswordLock) {
+ return hasEntries(TABLE_PASSWORD_ID);
+ }
+ }
+
+ /**
+ * @see WebViewDatabase#clearUsernamePassword
+ */
+ @Override
+ public void clearUsernamePassword() {
+ if (!checkInitialized()) {
+ return;
+ }
+
+ synchronized (mPasswordLock) {
+ sDatabase.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);
+ sDatabase.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 || !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 = sDatabase.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;
+ }
+ }
+
+ /**
+ * @see WebViewDatabase#hasHttpAuthUsernamePassword
+ */
+ @Override
+ public boolean hasHttpAuthUsernamePassword() {
+ synchronized (mHttpAuthLock) {
+ return hasEntries(TABLE_HTTPAUTH_ID);
+ }
+ }
+
+ /**
+ * @see WebViewDatabase#clearHttpAuthUsernamePassword
+ */
+ @Override
+ public void clearHttpAuthUsernamePassword() {
+ if (!checkInitialized()) {
+ return;
+ }
+
+ synchronized (mHttpAuthLock) {
+ sDatabase.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 = sDatabase.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 = sDatabase.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());
+ sDatabase.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 || !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 = sDatabase.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 = sDatabase.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;
+ }
+ }
+
+ /**
+ * @see WebViewDatabase#hasFormData
+ */
+ @Override
+ public boolean hasFormData() {
+ synchronized (mFormLock) {
+ return hasEntries(TABLE_FORMURL_ID);
+ }
+ }
+
+ /**
+ * @see WebViewDatabase#clearFormData
+ */
+ @Override
+ public void clearFormData() {
+ if (!checkInitialized()) {
+ return;
+ }
+
+ synchronized (mFormLock) {
+ sDatabase.delete(mTableNames[TABLE_FORMURL_ID], null, null);
+ sDatabase.delete(mTableNames[TABLE_FORMDATA_ID], null, null);
+ }
+ }
+}
diff --git a/core/java/android/webkit/WebViewFactoryProvider.java b/core/java/android/webkit/WebViewFactoryProvider.java
index a832b0a..1d302f1 100644
--- a/core/java/android/webkit/WebViewFactoryProvider.java
+++ b/core/java/android/webkit/WebViewFactoryProvider.java
@@ -16,6 +16,8 @@
package android.webkit;
+import android.content.Context;
+
/**
* This is the main entry-point into the WebView back end implementations, which the WebView
* proxy class uses to instantiate all the other objects as needed. The backend must provide an
@@ -63,21 +65,32 @@ public interface WebViewFactoryProvider {
/**
* Gets the singleton CookieManager instance for this WebView implementation. The
* implementation must return the same instance on subsequent calls.
- * @return the singleton CookieManager instance.
+ *
+ * @return the singleton CookieManager instance
*/
CookieManager getCookieManager();
/**
* Gets the singleton WebIconDatabase instance for this WebView implementation. The
* implementation must return the same instance on subsequent calls.
- * @return the singleton WebIconDatabase instance.
+ *
+ * @return the singleton WebIconDatabase instance
*/
WebIconDatabase getWebIconDatabase();
/**
* Gets the singleton WebStorage instance for this WebView implementation. The
* implementation must return the same instance on subsequent calls.
- * @return the singleton WebStorage instance.
+ *
+ * @return the singleton WebStorage instance
*/
WebStorage getWebStorage();
+
+ /**
+ * Gets the singleton WebViewDatabase instance for this WebView implementation. The
+ * implementation must return the same instance on subsequent calls.
+ *
+ * @return the singleton WebViewDatabase instance
+ */
+ WebViewDatabase getWebViewDatabase(Context context);
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 57a3012..e628bc1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1058,7 +1058,9 @@ public class RemoteViews implements Parcelable, Filter {
public ViewGroupAction(int viewId, RemoteViews nestedViews) {
this.viewId = viewId;
this.nestedViews = nestedViews;
- configureRemoteViewsAsChild(nestedViews);
+ if (nestedViews != null) {
+ configureRemoteViewsAsChild(nestedViews);
+ }
}
public ViewGroupAction(Parcel parcel, BitmapCache bitmapCache) {