Reimplement the settings to use async callbacks
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 @@
 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 @@
     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 @@
     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 @@
     }
 
     /**
+     * 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 @@
                 }
             }
         }
-        mLock.unlock();
     }
 
     /**
@@ -127,29 +172,31 @@
     }
 
     /**
+     * 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 @@
      */
     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;
         }
-        return allowed;
+        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));
+        }
     }
 
     /**
@@ -191,11 +234,7 @@
      */
     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 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 @@
         } else {
             postMessage(Message.obtain(null, CLEAR, origin));
         }
-        mLock.unlock();
     }
 
     /**
@@ -227,7 +264,6 @@
      */
     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 @@
         } 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.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 @@
     // 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;
 
-    private Set <String> mOrigins;
-    private HashMap <String, Long> mQuotas = new HashMap<String, Long>();
-    private HashMap <String, Long> mUsages = new HashMap<String, Long>();
+    // 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 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 long getQuota() {
             return mQuota;
         }
+
+        public long getUsage() {
+            return mUsage;
+        }
     }
 
     /**
      * @hide
-     * Message handler
+     * 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, webcore side
      */
     public void createHandler() {
         if (mHandler == null) {
@@ -117,6 +167,46 @@
                             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 @@
         }
     }
 
+    /*
+     * 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 @@
     }
 
     /**
+     * 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 @@
      * 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.View;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Map;
 import java.util.Set;
 
@@ -162,8 +163,10 @@
         // 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 @@
     // 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;
     }