summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Roard <nicolas@android.com>2009-09-22 18:44:52 +0100
committerNicolas Roard <nicolas@android.com>2009-09-22 18:44:52 +0100
commit6c24b4d10223cb522e6bdbf0e334f61e672f4366 (patch)
treed420d9f17d5dcb8c5dbbf37400dbe375a8d4074e
parent408cf85207a43c9169f9fb520a584d2858847d24 (diff)
downloadframeworks_base-6c24b4d10223cb522e6bdbf0e334f61e672f4366.zip
frameworks_base-6c24b4d10223cb522e6bdbf0e334f61e672f4366.tar.gz
frameworks_base-6c24b4d10223cb522e6bdbf0e334f61e672f4366.tar.bz2
Reimplement the settings to use async callbacks
-rwxr-xr-xcore/java/android/webkit/GeolocationPermissions.java147
-rw-r--r--core/java/android/webkit/ValueCallback.java29
-rw-r--r--core/java/android/webkit/WebStorage.java263
-rw-r--r--core/java/android/webkit/WebViewCore.java14
4 files changed, 311 insertions, 142 deletions
diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java
index e985ccc..c0cac01 100755
--- a/core/java/android/webkit/GeolocationPermissions.java
+++ b/core/java/android/webkit/GeolocationPermissions.java
@@ -19,10 +19,9 @@ package android.webkit;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
@@ -47,15 +46,13 @@ public final class GeolocationPermissions {
private static GeolocationPermissions sInstance;
private Handler mHandler;
+ private Handler mUIHandler;
// Members used to transfer the origins and permissions between threads.
private Set<String> mOrigins;
private boolean mAllowed;
private Set<String> mOriginsToClear;
private Set<String> mOriginsToAllow;
- private static Lock mLock = new ReentrantLock();
- private static boolean mUpdated;
- private static Condition mUpdatedCondition = mLock.newCondition();
// Message ids
static final int GET_ORIGINS = 0;
@@ -64,6 +61,15 @@ public final class GeolocationPermissions {
static final int ALLOW = 3;
static final int CLEAR_ALL = 4;
+ // Message ids on the UI thread
+ static final int RETURN_ORIGINS = 0;
+ static final int RETURN_ALLOWED = 1;
+
+ private static final String ORIGINS = "origins";
+ private static final String ORIGIN = "origin";
+ private static final String CALLBACK = "callback";
+ private static final String ALLOWED = "allowed";
+
/**
* Gets the singleton instance of the class.
*/
@@ -75,22 +81,62 @@ public final class GeolocationPermissions {
}
/**
+ * Creates the UI message handler. Must be called on the UI thread.
+ */
+ public void createUIHandler() {
+ if (mUIHandler == null) {
+ mUIHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ // Runs on the UI thread.
+ switch (msg.what) {
+ case RETURN_ORIGINS: {
+ Map values = (Map) msg.obj;
+ Set origins = (Set) values.get(ORIGINS);
+ ValueCallback<Set> callback = (ValueCallback<Set>) values.get(CALLBACK);
+ callback.onReceiveValue(origins);
+ } break;
+ case RETURN_ALLOWED: {
+ Map values = (Map) msg.obj;
+ Boolean allowed = (Boolean) values.get(ALLOWED);
+ ValueCallback<Boolean> callback = (ValueCallback<Boolean>) values.get(CALLBACK);
+ callback.onReceiveValue(allowed);
+ } break;
+ }
+ }
+ };
+ }
+ }
+
+ /**
* Creates the message handler. Must be called on the WebKit thread.
*/
public void createHandler() {
- mLock.lock();
if (mHandler == null) {
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
// Runs on the WebKit thread.
switch (msg.what) {
- case GET_ORIGINS:
+ case GET_ORIGINS: {
getOriginsImpl();
- break;
- case GET_ALLOWED:
- getAllowedImpl((String) msg.obj);
- break;
+ ValueCallback callback = (ValueCallback) msg.obj;
+ Set origins = new HashSet(mOrigins);
+ Map values = new HashMap<String, Object>();
+ values.put(CALLBACK, callback);
+ values.put(ORIGINS, origins);
+ postUIMessage(Message.obtain(null, RETURN_ORIGINS, values));
+ } break;
+ case GET_ALLOWED: {
+ Map values = (Map) msg.obj;
+ String origin = (String) values.get(ORIGIN);
+ ValueCallback callback = (ValueCallback) values.get(CALLBACK);
+ getAllowedImpl(origin);
+ Map retValues = new HashMap<String, Object>();
+ retValues.put(CALLBACK, callback);
+ retValues.put(ALLOWED, new Boolean(mAllowed));
+ postUIMessage(Message.obtain(null, RETURN_ALLOWED, retValues));
+ } break;
case CLEAR:
nativeClear((String) msg.obj);
break;
@@ -115,7 +161,6 @@ public final class GeolocationPermissions {
}
}
}
- mLock.unlock();
}
/**
@@ -127,29 +172,31 @@ public final class GeolocationPermissions {
}
/**
+ * Utility function to send a message to the handler on the UI thread
+ */
+ private void postUIMessage(Message msg) {
+ if (mUIHandler != null) {
+ mUIHandler.sendMessage(msg);
+ }
+ }
+
+ /**
* Gets the set of origins for which Geolocation permissions are stored.
* Note that we represent the origins as strings. These are created using
* WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules'
* (Database, Geolocation etc) do so, it's safe to match up origins for the
* purposes of displaying UI.
*/
- public Set getOrigins() {
- // Called on the UI thread.
- Set origins = null;
- mLock.lock();
- try {
- mUpdated = false;
- postMessage(Message.obtain(null, GET_ORIGINS));
- while (!mUpdated) {
- mUpdatedCondition.await();
+ public void getOrigins(ValueCallback<Set> callback) {
+ if (callback != null) {
+ if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+ getOriginsImpl();
+ Set origins = new HashSet(mOrigins);
+ callback.onReceiveValue(origins);
+ } else {
+ postMessage(Message.obtain(null, GET_ORIGINS, callback));
}
- origins = mOrigins;
- } catch (InterruptedException e) {
- Log.e(TAG, "Exception while waiting for update", e);
- } finally {
- mLock.unlock();
}
- return origins;
}
/**
@@ -157,33 +204,29 @@ public final class GeolocationPermissions {
*/
private void getOriginsImpl() {
// Called on the WebKit thread.
- mLock.lock();
mOrigins = nativeGetOrigins();
- mUpdated = true;
- mUpdatedCondition.signal();
- mLock.unlock();
}
/**
* Gets the permission state for the specified origin.
*/
- public boolean getAllowed(String origin) {
- // Called on the UI thread.
- boolean allowed = false;
- mLock.lock();
- try {
- mUpdated = false;
- postMessage(Message.obtain(null, GET_ALLOWED, origin));
- while (!mUpdated) {
- mUpdatedCondition.await();
- }
- allowed = mAllowed;
- } catch (InterruptedException e) {
- Log.e(TAG, "Exception while waiting for update", e);
- } finally {
- mLock.unlock();
+ public void getAllowed(String origin, ValueCallback<Boolean> callback) {
+ if (callback == null) {
+ return;
+ }
+ if (origin == null) {
+ callback.onReceiveValue(null);
+ return;
+ }
+ if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+ getAllowedImpl(origin);
+ callback.onReceiveValue(new Boolean(mAllowed));
+ } else {
+ Map values = new HashMap<String, Object>();
+ values.put(ORIGIN, origin);
+ values.put(CALLBACK, callback);
+ postMessage(Message.obtain(null, GET_ALLOWED, values));
}
- return allowed;
}
/**
@@ -191,11 +234,7 @@ public final class GeolocationPermissions {
*/
private void getAllowedImpl(String origin) {
// Called on the WebKit thread.
- mLock.lock();
mAllowed = nativeGetAllowed(origin);
- mUpdated = true;
- mUpdatedCondition.signal();
- mLock.unlock();
}
/**
@@ -205,7 +244,6 @@ public final class GeolocationPermissions {
*/
public void clear(String origin) {
// Called on the UI thread.
- mLock.lock();
if (mHandler == null) {
if (mOriginsToClear == null) {
mOriginsToClear = new HashSet<String>();
@@ -217,7 +255,6 @@ public final class GeolocationPermissions {
} else {
postMessage(Message.obtain(null, CLEAR, origin));
}
- mLock.unlock();
}
/**
@@ -227,7 +264,6 @@ public final class GeolocationPermissions {
*/
public void allow(String origin) {
// Called on the UI thread.
- mLock.lock();
if (mHandler == null) {
if (mOriginsToAllow == null) {
mOriginsToAllow = new HashSet<String>();
@@ -239,7 +275,6 @@ public final class GeolocationPermissions {
} else {
postMessage(Message.obtain(null, ALLOW, origin));
}
- mLock.unlock();
}
/**
diff --git a/core/java/android/webkit/ValueCallback.java b/core/java/android/webkit/ValueCallback.java
new file mode 100644
index 0000000..d8c5cdc
--- /dev/null
+++ b/core/java/android/webkit/ValueCallback.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2009 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;
+
+/**
+ * A callback interface used to returns values asynchronously
+ *
+ * @hide pending council approval
+ */
+public interface ValueCallback<T> {
+ /**
+ * Invoked when we have the result
+ */
+ public void onReceiveValue(T value);
+};
diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java
index ae560fb..0022248 100644
--- a/core/java/android/webkit/WebStorage.java
+++ b/core/java/android/webkit/WebStorage.java
@@ -20,9 +20,8 @@ import android.os.Handler;
import android.os.Message;
import android.util.Log;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
+import java.util.Collection;
+import java.util.Map;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -51,28 +50,41 @@ public final class WebStorage {
// Global instance of a WebStorage
private static WebStorage sWebStorage;
- // We keep the origins, quotas and usages as member values
- // that we protect via a lock and update in syncValues().
- // This is needed to transfer this data across threads.
- private static Lock mLock = new ReentrantLock();
- private static Condition mUpdateCondition = mLock.newCondition();
- private static boolean mUpdateAvailable;
-
// Message ids
static final int UPDATE = 0;
static final int SET_QUOTA_ORIGIN = 1;
static final int DELETE_ORIGIN = 2;
static final int DELETE_ALL = 3;
+ static final int GET_ORIGINS = 4;
+ static final int GET_USAGE_ORIGIN = 5;
+ static final int GET_QUOTA_ORIGIN = 6;
+
+ // Message ids on the UI thread
+ static final int RETURN_ORIGINS = 0;
+ static final int RETURN_USAGE_ORIGIN = 1;
+ static final int RETURN_QUOTA_ORIGIN = 2;
+
+ private static final String ORIGINS = "origins";
+ private static final String ORIGIN = "origin";
+ private static final String CALLBACK = "callback";
+ private static final String USAGE = "usage";
+ private static final String QUOTA = "quota";
- private Set <String> mOrigins;
- private HashMap <String, Long> mQuotas = new HashMap<String, Long>();
- private HashMap <String, Long> mUsages = new HashMap<String, Long>();
+ private Map <String, Origin> mOrigins;
private Handler mHandler = null;
+ private Handler mUIHandler = null;
- private static class Origin {
+ static class Origin {
String mOrigin = null;
long mQuota = 0;
+ long mUsage = 0;
+
+ public Origin(String origin, long quota, long usage) {
+ mOrigin = origin;
+ mQuota = quota;
+ mUsage = usage;
+ }
public Origin(String origin, long quota) {
mOrigin = origin;
@@ -90,11 +102,49 @@ public final class WebStorage {
public long getQuota() {
return mQuota;
}
+
+ public long getUsage() {
+ return mUsage;
+ }
+ }
+
+ /**
+ * @hide
+ * Message handler, UI side
+ */
+ public void createUIHandler() {
+ if (mUIHandler == null) {
+ mUIHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case RETURN_ORIGINS: {
+ Map values = (Map) msg.obj;
+ Map origins = (Map) values.get(ORIGINS);
+ ValueCallback<Map> callback = (ValueCallback<Map>) values.get(CALLBACK);
+ callback.onReceiveValue(origins);
+ } break;
+
+ case RETURN_USAGE_ORIGIN: {
+ Map values = (Map) msg.obj;
+ ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK);
+ callback.onReceiveValue((Long)values.get(USAGE));
+ } break;
+
+ case RETURN_QUOTA_ORIGIN: {
+ Map values = (Map) msg.obj;
+ ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK);
+ callback.onReceiveValue((Long)values.get(QUOTA));
+ } break;
+ }
+ }
+ };
+ }
}
/**
* @hide
- * Message handler
+ * Message handler, webcore side
*/
public void createHandler() {
if (mHandler == null) {
@@ -117,6 +167,46 @@ public final class WebStorage {
nativeDeleteAllData();
break;
+ case GET_ORIGINS: {
+ syncValues();
+ ValueCallback callback = (ValueCallback) msg.obj;
+ Map origins = new HashMap(mOrigins);
+ Map values = new HashMap<String, Object>();
+ values.put(CALLBACK, callback);
+ values.put(ORIGINS, origins);
+ postUIMessage(Message.obtain(null, RETURN_ORIGINS, values));
+ } break;
+
+ case GET_USAGE_ORIGIN: {
+ syncValues();
+ Map values = (Map) msg.obj;
+ String origin = (String) values.get(ORIGIN);
+ ValueCallback callback = (ValueCallback) values.get(CALLBACK);
+ Origin website = mOrigins.get(origin);
+ Map retValues = new HashMap<String, Object>();
+ retValues.put(CALLBACK, callback);
+ if (website != null) {
+ long usage = website.getUsage();
+ retValues.put(USAGE, new Long(usage));
+ }
+ postUIMessage(Message.obtain(null, RETURN_USAGE_ORIGIN, retValues));
+ } break;
+
+ case GET_QUOTA_ORIGIN: {
+ syncValues();
+ Map values = (Map) msg.obj;
+ String origin = (String) values.get(ORIGIN);
+ ValueCallback callback = (ValueCallback) values.get(CALLBACK);
+ Origin website = mOrigins.get(origin);
+ Map retValues = new HashMap<String, Object>();
+ retValues.put(CALLBACK, callback);
+ if (website != null) {
+ long quota = website.getQuota();
+ retValues.put(QUOTA, new Long(quota));
+ }
+ postUIMessage(Message.obtain(null, RETURN_QUOTA_ORIGIN, retValues));
+ } break;
+
case UPDATE:
syncValues();
break;
@@ -126,82 +216,91 @@ public final class WebStorage {
}
}
+ /*
+ * When calling getOrigins(), getUsageForOrigin() and getQuotaForOrigin(),
+ * we need to get the values from webcore, but we cannot block while doing so
+ * as we used to do, as this could result in a full deadlock (other webcore
+ * messages received while we are still blocked here, see http://b/2127737).
+ *
+ * We have to do everything asynchronously, by providing a callback function.
+ * We post a message on the webcore thread (mHandler) that will get the result
+ * from webcore, and we post it back on the UI thread (using mUIHandler).
+ * We can then use the callback function to return the value.
+ */
+
/**
* @hide
* Returns a list of origins having a database
*/
- public Set getOrigins() {
- Set ret = null;
- mLock.lock();
- try {
- mUpdateAvailable = false;
- update();
- while (!mUpdateAvailable) {
- mUpdateCondition.await();
+ public void getOrigins(ValueCallback<Map> callback) {
+ if (callback != null) {
+ if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+ syncValues();
+ callback.onReceiveValue(mOrigins);
+ } else {
+ postMessage(Message.obtain(null, GET_ORIGINS, callback));
}
- ret = mOrigins;
- } catch (InterruptedException e) {
- Log.e(TAG, "Exception while waiting on the updated origins", e);
- } finally {
- mLock.unlock();
}
- return ret;
+ }
+
+ /**
+ * Returns a list of origins having a database
+ * should only be called from WebViewCore.
+ */
+ Collection<Origin> getOriginsSync() {
+ if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+ update();
+ return mOrigins.values();
+ }
+ return null;
}
/**
* @hide
* Returns the use for a given origin
*/
- public long getUsageForOrigin(String origin) {
- long ret = 0;
+ public void getUsageForOrigin(String origin, ValueCallback<Long> callback) {
+ if (callback == null) {
+ return;
+ }
if (origin == null) {
- return ret;
+ callback.onReceiveValue(null);
+ return;
}
- mLock.lock();
- try {
- mUpdateAvailable = false;
- update();
- while (!mUpdateAvailable) {
- mUpdateCondition.await();
- }
- Long usage = mUsages.get(origin);
- if (usage != null) {
- ret = usage.longValue();
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "Exception while waiting on the updated origins", e);
- } finally {
- mLock.unlock();
+ if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+ syncValues();
+ Origin website = mOrigins.get(origin);
+ callback.onReceiveValue(new Long(website.getUsage()));
+ } else {
+ HashMap values = new HashMap<String, Object>();
+ values.put(ORIGIN, origin);
+ values.put(CALLBACK, callback);
+ postMessage(Message.obtain(null, GET_USAGE_ORIGIN, values));
}
- return ret;
}
/**
* @hide
* Returns the quota for a given origin
*/
- public long getQuotaForOrigin(String origin) {
- long ret = 0;
+ public void getQuotaForOrigin(String origin, ValueCallback<Long> callback) {
+ if (callback == null) {
+ return;
+ }
if (origin == null) {
- return ret;
+ callback.onReceiveValue(null);
+ return;
}
- mLock.lock();
- try {
- mUpdateAvailable = false;
- update();
- while (!mUpdateAvailable) {
- mUpdateCondition.await();
- }
- Long quota = mQuotas.get(origin);
- if (quota != null) {
- ret = quota.longValue();
- }
- } catch (InterruptedException e) {
- Log.e(TAG, "Exception while waiting on the updated origins", e);
- } finally {
- mLock.unlock();
+ if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
+ syncValues();
+ Origin website = mOrigins.get(origin);
+ callback.onReceiveValue(new Long(website.getUsage()));
+ } else {
+ HashMap values = new HashMap<String, Object>();
+ values.put(ORIGIN, origin);
+ values.put(CALLBACK, callback);
+ postMessage(Message.obtain(null, GET_QUOTA_ORIGIN, values));
}
- return ret;
}
/**
@@ -256,6 +355,15 @@ public final class WebStorage {
}
/**
+ * Utility function to send a message to the handler on the UI thread
+ */
+ private void postUIMessage(Message msg) {
+ if (mUIHandler != null) {
+ mUIHandler.sendMessage(msg);
+ }
+ }
+
+ /**
* @hide
* Get the global instance of WebStorage.
* @return A single instance of WebStorage.
@@ -284,21 +392,14 @@ public final class WebStorage {
* set the local values with the current ones
*/
private void syncValues() {
- mLock.lock();
- Set tmp = nativeGetOrigins();
- mOrigins = new HashSet<String>();
- mQuotas.clear();
- mUsages.clear();
- Iterator<String> iter = tmp.iterator();
- while (iter.hasNext()) {
- String origin = iter.next();
- mOrigins.add(origin);
- mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin)));
- mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin)));
+ Set<String> tmp = nativeGetOrigins();
+ mOrigins = new HashMap<String, Origin>();
+ for (String origin : tmp) {
+ Origin website = new Origin(origin,
+ nativeGetUsageForOrigin(origin),
+ nativeGetQuotaForOrigin(origin));
+ mOrigins.put(origin, website);
}
- mUpdateAvailable = true;
- mUpdateCondition.signal();
- mLock.unlock();
}
// Native functions
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index e734444..e7654cc 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -37,6 +37,7 @@ import android.view.SurfaceView;
import android.view.View;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Map;
import java.util.Set;
@@ -162,8 +163,10 @@ final class WebViewCore {
// The WebIconDatabase needs to be initialized within the UI thread so
// just request the instance here.
WebIconDatabase.getInstance();
- // Create the WebStorage singleton
- WebStorage.getInstance();
+ // Create the WebStorage singleton and the UI handler
+ WebStorage.getInstance().createUIHandler();
+ // Create the UI handler for GeolocationPermissions
+ GeolocationPermissions.getInstance().createUIHandler();
// Send a message to initialize the WebViewCore.
Message init = sWebCoreHandler.obtainMessage(
WebCoreThread.INITIALIZE, this);
@@ -1519,13 +1522,14 @@ final class WebViewCore {
// callbacks. Computes the sum of database quota for all origins.
private long getUsedQuota() {
WebStorage webStorage = WebStorage.getInstance();
- Set<String> origins = webStorage.getOrigins();
+ Collection<WebStorage.Origin> origins = webStorage.getOriginsSync();
+
if (origins == null) {
return 0;
}
long usedQuota = 0;
- for (String origin : origins) {
- usedQuota += webStorage.getQuotaForOrigin(origin);
+ for (WebStorage.Origin website : origins) {
+ usedQuota += website.getQuota();
}
return usedQuota;
}