GeolocationPermissions.java revision d875ce6dac3c2e9a671c121c80b40d2536cbb2af
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.os.Handler;
20import android.os.Message;
21import android.util.Log;
22import java.util.concurrent.locks.Condition;
23import java.util.concurrent.locks.Lock;
24import java.util.concurrent.locks.ReentrantLock;
25import java.util.HashSet;
26import java.util.Set;
27
28
29/**
30 * Implements the Java side of GeolocationPermissions. Simply marshalls calls
31 * from the UI thread to the WebKit thread.
32 * @hide
33 */
34public final class GeolocationPermissions {
35    /**
36     * Callback interface used by the browser to report a Geolocation permission
37     * state set by the user in response to a permissions prompt.
38     */
39    public interface Callback {
40        public void invoke(String origin, boolean allow, boolean remember);
41    };
42
43    // Log tag
44    private static final String TAG = "geolocationPermissions";
45
46    // Global instance
47    private static GeolocationPermissions sInstance;
48
49    private Handler mHandler;
50
51    // Members used to transfer the origins and permissions between threads.
52    private Set<String> mOrigins;
53    private boolean mAllowed;
54    private Set<String> mOriginsToClear;
55    private Set<String> mOriginsToAllow;
56    private static Lock mLock = new ReentrantLock();
57    private static boolean mUpdated;
58    private static Condition mUpdatedCondition = mLock.newCondition();
59
60    // Message ids
61    static final int GET_ORIGINS = 0;
62    static final int GET_ALLOWED = 1;
63    static final int CLEAR = 2;
64    static final int ALLOW = 3;
65    static final int CLEAR_ALL = 4;
66
67    /**
68     * Gets the singleton instance of the class.
69     */
70    public static GeolocationPermissions getInstance() {
71      if (sInstance == null) {
72          sInstance = new GeolocationPermissions();
73      }
74      return sInstance;
75    }
76
77    /**
78     * Creates the message handler. Must be called on the WebKit thread.
79     */
80    public void createHandler() {
81        mLock.lock();
82        if (mHandler == null) {
83            mHandler = new Handler() {
84                @Override
85                public void handleMessage(Message msg) {
86                    // Runs on the WebKit thread.
87                    switch (msg.what) {
88                        case GET_ORIGINS:
89                            getOriginsImpl();
90                            break;
91                        case GET_ALLOWED:
92                            getAllowedImpl((String) msg.obj);
93                            break;
94                        case CLEAR:
95                            nativeClear((String) msg.obj);
96                            break;
97                        case ALLOW:
98                            nativeAllow((String) msg.obj);
99                            break;
100                        case CLEAR_ALL:
101                            nativeClearAll();
102                            break;
103                    }
104                }
105            };
106
107            if (mOriginsToClear != null) {
108                for (String origin : mOriginsToClear) {
109                    nativeClear(origin);
110                }
111            }
112            if (mOriginsToAllow != null) {
113                for (String origin : mOriginsToAllow) {
114                    nativeAllow(origin);
115                }
116            }
117        }
118        mLock.unlock();
119    }
120
121    /**
122     * Utility function to send a message to our handler.
123     */
124    private void postMessage(Message msg) {
125        assert(mHandler != null);
126        mHandler.sendMessage(msg);
127    }
128
129    /**
130     * Gets the set of origins for which Geolocation permissions are stored.
131     * Note that we represent the origins as strings. These are created using
132     * WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules'
133     * (Database, Geolocation etc) do so, it's safe to match up origins for the
134     * purposes of displaying UI.
135     */
136    public Set getOrigins() {
137        // Called on the UI thread.
138        Set origins = null;
139        mLock.lock();
140        try {
141            mUpdated = false;
142            postMessage(Message.obtain(null, GET_ORIGINS));
143            while (!mUpdated) {
144                mUpdatedCondition.await();
145            }
146            origins = mOrigins;
147        } catch (InterruptedException e) {
148            Log.e(TAG, "Exception while waiting for update", e);
149        } finally {
150            mLock.unlock();
151        }
152        return origins;
153    }
154
155    /**
156     * Helper method to get the set of origins.
157     */
158    private void getOriginsImpl() {
159        // Called on the WebKit thread.
160        mLock.lock();
161        mOrigins = nativeGetOrigins();
162        mUpdated = true;
163        mUpdatedCondition.signal();
164        mLock.unlock();
165    }
166
167    /**
168     * Gets the permission state for the specified origin.
169     */
170    public boolean getAllowed(String origin) {
171        // Called on the UI thread.
172        boolean allowed = false;
173        mLock.lock();
174        try {
175            mUpdated = false;
176            postMessage(Message.obtain(null, GET_ALLOWED, origin));
177            while (!mUpdated) {
178                mUpdatedCondition.await();
179            }
180            allowed = mAllowed;
181        } catch (InterruptedException e) {
182            Log.e(TAG, "Exception while waiting for update", e);
183        } finally {
184            mLock.unlock();
185        }
186        return allowed;
187    }
188
189    /**
190     * Helper method to get the permission state.
191     */
192    private void getAllowedImpl(String origin) {
193        // Called on the WebKit thread.
194        mLock.lock();
195        mAllowed = nativeGetAllowed(origin);
196        mUpdated = true;
197        mUpdatedCondition.signal();
198        mLock.unlock();
199    }
200
201    /**
202     * Clears the permission state for the specified origin. This method may be
203     * called before the WebKit thread has intialized the message handler.
204     * Messages will be queued until this time.
205     */
206    public void clear(String origin) {
207        // Called on the UI thread.
208        mLock.lock();
209        if (mHandler == null) {
210            if (mOriginsToClear == null) {
211                mOriginsToClear = new HashSet<String>();
212            }
213            mOriginsToClear.add(origin);
214            if (mOriginsToAllow != null) {
215                mOriginsToAllow.remove(origin);
216            }
217        } else {
218            postMessage(Message.obtain(null, CLEAR, origin));
219        }
220        mLock.unlock();
221    }
222
223    /**
224     * Allows the specified origin. This method may be called before the WebKit
225     * thread has intialized the message handler. Messages will be queued until
226     * this time.
227     */
228    public void allow(String origin) {
229        // Called on the UI thread.
230        mLock.lock();
231        if (mHandler == null) {
232            if (mOriginsToAllow == null) {
233                mOriginsToAllow = new HashSet<String>();
234            }
235            mOriginsToAllow.add(origin);
236            if (mOriginsToClear != null) {
237                mOriginsToClear.remove(origin);
238            }
239        } else {
240            postMessage(Message.obtain(null, ALLOW, origin));
241        }
242        mLock.unlock();
243    }
244
245    /**
246     * Clears the permission state for all origins.
247     */
248    public void clearAll() {
249        // Called on the UI thread.
250        postMessage(Message.obtain(null, CLEAR_ALL));
251    }
252
253    // Native functions, run on the WebKit thread.
254    private static native Set nativeGetOrigins();
255    private static native boolean nativeGetAllowed(String origin);
256    private static native void nativeClear(String origin);
257    private static native void nativeAllow(String origin);
258    private static native void nativeClearAll();
259}
260