1/*
2 * Copyright (C) 2012 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;
21
22import java.util.HashMap;
23import java.util.Map;
24import java.util.Set;
25import java.util.Vector;
26
27// This class is the Java counterpart of the WebKit C++ GeolocationPermissions
28// class. It simply marshals calls from the UI thread to the WebKit thread.
29final class GeolocationPermissionsClassic extends GeolocationPermissions {
30    private Handler mHandler;
31    private Handler mUIHandler;
32
33    // A queue to store messages until the handler is ready.
34    private Vector<Message> mQueuedMessages;
35
36    // Message ids
37    static final int GET_ORIGINS = 0;
38    static final int GET_ALLOWED = 1;
39    static final int CLEAR = 2;
40    static final int ALLOW = 3;
41    static final int CLEAR_ALL = 4;
42
43    // Message ids on the UI thread
44    static final int RETURN_ORIGINS = 0;
45    static final int RETURN_ALLOWED = 1;
46
47    private static final String ORIGINS = "origins";
48    private static final String ORIGIN = "origin";
49    private static final String CALLBACK = "callback";
50    private static final String ALLOWED = "allowed";
51
52    // Global instance
53    private static GeolocationPermissionsClassic sInstance;
54
55    public static GeolocationPermissionsClassic getInstance() {
56        if (sInstance == null) {
57            sInstance = new GeolocationPermissionsClassic();
58        }
59        return sInstance;
60      }
61
62    /**
63     * Creates the UI message handler. Must be called on the UI thread.
64     * @hide
65     */
66    public void createUIHandler() {
67        if (mUIHandler == null) {
68            mUIHandler = new Handler() {
69                @Override
70                public void handleMessage(Message msg) {
71                    // Runs on the UI thread.
72                    switch (msg.what) {
73                        case RETURN_ORIGINS: {
74                            Map values = (Map) msg.obj;
75                            Set<String> origins = (Set<String>) values.get(ORIGINS);
76                            ValueCallback<Set<String> > callback = (ValueCallback<Set<String> >) values.get(CALLBACK);
77                            callback.onReceiveValue(origins);
78                        } break;
79                        case RETURN_ALLOWED: {
80                            Map values = (Map) msg.obj;
81                            Boolean allowed = (Boolean) values.get(ALLOWED);
82                            ValueCallback<Boolean> callback = (ValueCallback<Boolean>) values.get(CALLBACK);
83                            callback.onReceiveValue(allowed);
84                        } break;
85                    }
86                }
87            };
88        }
89    }
90
91    /**
92     * Creates the message handler. Must be called on the WebKit thread.
93     * @hide
94     */
95    public synchronized void createHandler() {
96        if (mHandler == null) {
97            mHandler = new Handler() {
98                @Override
99                public void handleMessage(Message msg) {
100                    // Runs on the WebKit thread.
101                    switch (msg.what) {
102                        case GET_ORIGINS: {
103                            Set origins = nativeGetOrigins();
104                            ValueCallback callback = (ValueCallback) msg.obj;
105                            Map values = new HashMap<String, Object>();
106                            values.put(CALLBACK, callback);
107                            values.put(ORIGINS, origins);
108                            postUIMessage(Message.obtain(null, RETURN_ORIGINS, values));
109                            } break;
110                        case GET_ALLOWED: {
111                            Map values = (Map) msg.obj;
112                            String origin = (String) values.get(ORIGIN);
113                            ValueCallback callback = (ValueCallback) values.get(CALLBACK);
114                            boolean allowed = nativeGetAllowed(origin);
115                            Map retValues = new HashMap<String, Object>();
116                            retValues.put(CALLBACK, callback);
117                            retValues.put(ALLOWED, Boolean.valueOf(allowed));
118                            postUIMessage(Message.obtain(null, RETURN_ALLOWED, retValues));
119                            } break;
120                        case CLEAR:
121                            nativeClear((String) msg.obj);
122                            break;
123                        case ALLOW:
124                            nativeAllow((String) msg.obj);
125                            break;
126                        case CLEAR_ALL:
127                            nativeClearAll();
128                            break;
129                    }
130                }
131            };
132
133            // Handle the queued messages
134            if (mQueuedMessages != null) {
135                while (!mQueuedMessages.isEmpty()) {
136                    mHandler.sendMessage(mQueuedMessages.remove(0));
137                }
138                mQueuedMessages = null;
139            }
140        }
141    }
142
143    /**
144     * Utility function to send a message to our handler.
145     */
146    private synchronized void postMessage(Message msg) {
147        if (mHandler == null) {
148            if (mQueuedMessages == null) {
149                mQueuedMessages = new Vector<Message>();
150            }
151            mQueuedMessages.add(msg);
152        } else {
153            mHandler.sendMessage(msg);
154        }
155    }
156
157    /**
158     * Utility function to send a message to the handler on the UI thread
159     */
160    private void postUIMessage(Message msg) {
161        if (mUIHandler != null) {
162            mUIHandler.sendMessage(msg);
163        }
164    }
165
166    // Note that we represent the origins as strings. These are created using
167    // WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules'
168    // (Database, Geolocation etc) do so, it's safe to match up origins based
169    // on this string.
170    @Override
171    public void getOrigins(ValueCallback<Set<String> > callback) {
172        if (callback != null) {
173            if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
174                Set origins = nativeGetOrigins();
175                callback.onReceiveValue(origins);
176            } else {
177                postMessage(Message.obtain(null, GET_ORIGINS, callback));
178            }
179        }
180    }
181
182    @Override
183    public void getAllowed(String origin, ValueCallback<Boolean> callback) {
184        if (callback == null) {
185            return;
186        }
187        if (origin == null) {
188            callback.onReceiveValue(null);
189            return;
190        }
191        if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) {
192            boolean allowed = nativeGetAllowed(origin);
193            callback.onReceiveValue(Boolean.valueOf(allowed));
194        } else {
195            Map values = new HashMap<String, Object>();
196            values.put(ORIGIN, origin);
197            values.put(CALLBACK, callback);
198            postMessage(Message.obtain(null, GET_ALLOWED, values));
199        }
200    }
201
202    // This method may be called before the WebKit
203    // thread has intialized the message handler. Messages will be queued until
204    // this time.
205    @Override
206    public void clear(String origin) {
207        // Called on the UI thread.
208        postMessage(Message.obtain(null, CLEAR, origin));
209    }
210
211    // This method may be called before the WebKit
212    // thread has intialized the message handler. Messages will be queued until
213    // this time.
214    @Override
215    public void allow(String origin) {
216        // Called on the UI thread.
217        postMessage(Message.obtain(null, ALLOW, origin));
218    }
219
220    @Override
221    public void clearAll() {
222        // Called on the UI thread.
223        postMessage(Message.obtain(null, CLEAR_ALL));
224    }
225
226    GeolocationPermissionsClassic() {}
227
228    // Native functions, run on the WebKit thread.
229    private static native Set nativeGetOrigins();
230    private static native boolean nativeGetAllowed(String origin);
231    private static native void nativeClear(String origin);
232    private static native void nativeAllow(String origin);
233    private static native void nativeClearAll();
234}
235