GeolocationPermissions.java revision e4b2d4dc7db426052d1dfebc40f6b64a001b6d73
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.HashMap; 23import java.util.HashSet; 24import java.util.Map; 25import java.util.Set; 26 27 28/** 29 * This class is used to get Geolocation permissions from, and set them on the 30 * WebView. For example, it could be used to allow a user to manage Geolocation 31 * permissions from a browser's UI. 32 * 33 * Permissions are managed on a per-origin basis, as required by the 34 * Geolocation spec - http://dev.w3.org/geo/api/spec-source.html. An origin 35 * specifies the scheme, host and port of particular frame. An origin is 36 * represented here as a string, using the output of 37 * WebCore::SecurityOrigin::toString. 38 * 39 * This class is the Java counterpart of the WebKit C++ GeolocationPermissions 40 * class. It simply marshalls calls from the UI thread to the WebKit thread. 41 * 42 * Within WebKit, Geolocation permissions may be applied either temporarily 43 * (for the duration of the page) or permanently. This class deals only with 44 * permanent permissions. 45 */ 46public final class GeolocationPermissions { 47 /** 48 * Callback interface used by the browser to report a Geolocation permission 49 * state set by the user in response to a permissions prompt. 50 */ 51 public interface Callback { 52 public void invoke(String origin, boolean allow, boolean remember); 53 }; 54 55 // Log tag 56 private static final String TAG = "geolocationPermissions"; 57 58 // Global instance 59 private static GeolocationPermissions sInstance; 60 61 private Handler mHandler; 62 private Handler mUIHandler; 63 64 // Members used to transfer the origins and permissions between threads. 65 private Set<String> mOrigins; 66 private boolean mAllowed; 67 private Set<String> mOriginsToClear; 68 private Set<String> mOriginsToAllow; 69 70 // Message ids 71 static final int GET_ORIGINS = 0; 72 static final int GET_ALLOWED = 1; 73 static final int CLEAR = 2; 74 static final int ALLOW = 3; 75 static final int CLEAR_ALL = 4; 76 77 // Message ids on the UI thread 78 static final int RETURN_ORIGINS = 0; 79 static final int RETURN_ALLOWED = 1; 80 81 private static final String ORIGINS = "origins"; 82 private static final String ORIGIN = "origin"; 83 private static final String CALLBACK = "callback"; 84 private static final String ALLOWED = "allowed"; 85 86 /** 87 * Gets the singleton instance of the class. 88 */ 89 public static GeolocationPermissions getInstance() { 90 if (sInstance == null) { 91 sInstance = new GeolocationPermissions(); 92 } 93 return sInstance; 94 } 95 96 /** 97 * Creates the UI message handler. Must be called on the UI thread. 98 * @hide 99 */ 100 public void createUIHandler() { 101 if (mUIHandler == null) { 102 mUIHandler = new Handler() { 103 @Override 104 public void handleMessage(Message msg) { 105 // Runs on the UI thread. 106 switch (msg.what) { 107 case RETURN_ORIGINS: { 108 Map values = (Map) msg.obj; 109 Set<String> origins = (Set<String>) values.get(ORIGINS); 110 ValueCallback<Set<String> > callback = (ValueCallback<Set<String> >) values.get(CALLBACK); 111 callback.onReceiveValue(origins); 112 } break; 113 case RETURN_ALLOWED: { 114 Map values = (Map) msg.obj; 115 Boolean allowed = (Boolean) values.get(ALLOWED); 116 ValueCallback<Boolean> callback = (ValueCallback<Boolean>) values.get(CALLBACK); 117 callback.onReceiveValue(allowed); 118 } break; 119 } 120 } 121 }; 122 } 123 } 124 125 /** 126 * Creates the message handler. Must be called on the WebKit thread. 127 * @hide 128 */ 129 public synchronized void createHandler() { 130 if (mHandler == null) { 131 mHandler = new Handler() { 132 @Override 133 public void handleMessage(Message msg) { 134 // Runs on the WebKit thread. 135 switch (msg.what) { 136 case GET_ORIGINS: { 137 getOriginsImpl(); 138 ValueCallback callback = (ValueCallback) msg.obj; 139 Map values = new HashMap<String, Object>(); 140 values.put(CALLBACK, callback); 141 values.put(ORIGINS, mOrigins); 142 postUIMessage(Message.obtain(null, RETURN_ORIGINS, values)); 143 } break; 144 case GET_ALLOWED: { 145 Map values = (Map) msg.obj; 146 String origin = (String) values.get(ORIGIN); 147 ValueCallback callback = (ValueCallback) values.get(CALLBACK); 148 getAllowedImpl(origin); 149 Map retValues = new HashMap<String, Object>(); 150 retValues.put(CALLBACK, callback); 151 retValues.put(ALLOWED, new Boolean(mAllowed)); 152 postUIMessage(Message.obtain(null, RETURN_ALLOWED, retValues)); 153 } break; 154 case CLEAR: 155 nativeClear((String) msg.obj); 156 break; 157 case ALLOW: 158 nativeAllow((String) msg.obj); 159 break; 160 case CLEAR_ALL: 161 nativeClearAll(); 162 break; 163 } 164 } 165 }; 166 167 if (mOriginsToClear != null) { 168 for (String origin : mOriginsToClear) { 169 nativeClear(origin); 170 } 171 } 172 if (mOriginsToAllow != null) { 173 for (String origin : mOriginsToAllow) { 174 nativeAllow(origin); 175 } 176 } 177 } 178 } 179 180 /** 181 * Utility function to send a message to our handler. 182 */ 183 private synchronized void postMessage(Message msg) { 184 assert(mHandler != null); 185 mHandler.sendMessage(msg); 186 } 187 188 /** 189 * Utility function to send a message to the handler on the UI thread 190 */ 191 private void postUIMessage(Message msg) { 192 if (mUIHandler != null) { 193 mUIHandler.sendMessage(msg); 194 } 195 } 196 197 /** 198 * Gets the set of origins for which Geolocation permissions are stored. 199 * Note that we represent the origins as strings. These are created using 200 * WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules' 201 * (Database, Geolocation etc) do so, it's safe to match up origins based 202 * on this string. 203 * 204 * Callback is a ValueCallback object whose onReceiveValue method will be 205 * called asynchronously with the set of origins. 206 */ 207 public void getOrigins(ValueCallback<Set<String> > callback) { 208 if (callback != null) { 209 if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { 210 getOriginsImpl(); 211 callback.onReceiveValue(mOrigins); 212 } else { 213 postMessage(Message.obtain(null, GET_ORIGINS, callback)); 214 } 215 } 216 } 217 218 /** 219 * Helper method to get the set of origins. 220 */ 221 private void getOriginsImpl() { 222 // Called on the WebKit thread. 223 mOrigins = nativeGetOrigins(); 224 } 225 226 /** 227 * Gets the permission state for the specified origin. 228 * 229 * Callback is a ValueCallback object whose onReceiveValue method will be 230 * called asynchronously with the permission state for the origin. 231 */ 232 public void getAllowed(String origin, ValueCallback<Boolean> callback) { 233 if (callback == null) { 234 return; 235 } 236 if (origin == null) { 237 callback.onReceiveValue(null); 238 return; 239 } 240 if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { 241 getAllowedImpl(origin); 242 callback.onReceiveValue(new Boolean(mAllowed)); 243 } else { 244 Map values = new HashMap<String, Object>(); 245 values.put(ORIGIN, origin); 246 values.put(CALLBACK, callback); 247 postMessage(Message.obtain(null, GET_ALLOWED, values)); 248 } 249 } 250 251 /** 252 * Helper method to get the permission state for the specified origin. 253 */ 254 private void getAllowedImpl(String origin) { 255 // Called on the WebKit thread. 256 mAllowed = nativeGetAllowed(origin); 257 } 258 259 /** 260 * Clears the permission state for the specified origin. This method may be 261 * called before the WebKit thread has intialized the message handler. 262 * Messages will be queued until this time. 263 */ 264 public void clear(String origin) { 265 // Called on the UI thread. 266 if (mHandler == null) { 267 if (mOriginsToClear == null) { 268 mOriginsToClear = new HashSet<String>(); 269 } 270 mOriginsToClear.add(origin); 271 if (mOriginsToAllow != null) { 272 mOriginsToAllow.remove(origin); 273 } 274 } else { 275 postMessage(Message.obtain(null, CLEAR, origin)); 276 } 277 } 278 279 /** 280 * Allows the specified origin. This method may be called before the WebKit 281 * thread has intialized the message handler. Messages will be queued until 282 * this time. 283 */ 284 public void allow(String origin) { 285 // Called on the UI thread. 286 if (mHandler == null) { 287 if (mOriginsToAllow == null) { 288 mOriginsToAllow = new HashSet<String>(); 289 } 290 mOriginsToAllow.add(origin); 291 if (mOriginsToClear != null) { 292 mOriginsToClear.remove(origin); 293 } 294 } else { 295 postMessage(Message.obtain(null, ALLOW, origin)); 296 } 297 } 298 299 /** 300 * Clears the permission state for all origins. 301 */ 302 public void clearAll() { 303 // Called on the UI thread. 304 postMessage(Message.obtain(null, CLEAR_ALL)); 305 } 306 307 // Native functions, run on the WebKit thread. 308 private static native Set nativeGetOrigins(); 309 private static native boolean nativeGetAllowed(String origin); 310 private static native void nativeClear(String origin); 311 private static native void nativeAllow(String origin); 312 private static native void nativeClearAll(); 313} 314