GeofenceHardware.java revision 17ff2b2b3a28da2f34e82ce1a623865fc249e87c
1/* 2 * Copyright (C) 2013 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 */ 16package android.hardware.location; 17 18import android.location.Location; 19import android.os.RemoteException; 20 21import java.lang.ref.WeakReference; 22import java.util.HashMap; 23 24/** 25 * This class handles geofences managed by various hardware subsystems. It contains 26 * the public APIs that is needed to accomplish the task. 27 * 28 * <p>The APIs should not be called directly by the app developers. A higher level api 29 * which abstracts the hardware should be used instead. All the checks are done by the higher 30 * level public API. Any needed locking should be handled by the higher level API. 31 * 32 * <p> There are 3 states associated with a Geofence: Inside, Outside, Unknown. 33 * There are 3 transitions: {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED}, 34 * {@link #GEOFENCE_UNCERTAIN}. The APIs only expose the transitions. 35 * 36 * <p> Inside state: The hardware subsystem is reasonably confident that the user is inside 37 * the geofence. Outside state: The hardware subsystem is reasonably confident that the user 38 * is outside the geofence Unknown state: Unknown state can be interpreted as a state in which the 39 * monitoring subsystem isn't confident enough that the user is either inside or 40 * outside the Geofence. If the accuracy does not improve for a sufficient period of time, 41 * the {@link #GEOFENCE_UNCERTAIN} transition would be triggered. If the accuracy improves later, 42 * an appropriate transition would be triggered. The "reasonably confident" parameter 43 * depends on the hardware system and the positioning algorithms used. 44 * For instance, {@link #MONITORING_TYPE_GPS_HARDWARE} uses 95% as a confidence level. 45 */ 46public final class GeofenceHardware { 47 private IGeofenceHardware mService; 48 49 // Hardware systems that do geofence monitoring. 50 static final int NUM_MONITORS = 2; 51 52 /** 53 * Constant for geofence monitoring done by the GPS hardware. 54 */ 55 public static final int MONITORING_TYPE_GPS_HARDWARE = 0; 56 57 /** 58 * Constant for geofence monitoring done by the Fused hardware. 59 */ 60 public static final int MONITORING_TYPE_FUSED_HARDWARE = 1; 61 62 /** 63 * Constant to indiciate that the monitoring system is currently 64 * available for monitoring geofences. 65 */ 66 public static final int MONITOR_CURRENTLY_AVAILABLE = 0; 67 68 /** 69 * Constant to indiciate that the monitoring system is currently 70 * unavailable for monitoring geofences. 71 */ 72 public static final int MONITOR_CURRENTLY_UNAVAILABLE = 1; 73 74 /** 75 * Constant to indiciate that the monitoring system is unsupported 76 * for hardware geofence monitoring. 77 */ 78 public static final int MONITOR_UNSUPPORTED = 2; 79 80 // The following constants need to match geofence flags in gps.h and fused_location.h 81 /** 82 * The constant to indicate that the user has entered the geofence. 83 */ 84 public static final int GEOFENCE_ENTERED = 1<<0L; 85 86 /** 87 * The constant to indicate that the user has exited the geofence. 88 */ 89 public static final int GEOFENCE_EXITED = 1<<1L; 90 91 /** 92 * The constant to indicate that the user is uncertain with respect to a 93 * geofence. 94 */ 95 public static final int GEOFENCE_UNCERTAIN = 1<<2L; 96 97 /** 98 * The constant used to indicate success of the particular geofence call 99 */ 100 public static final int GEOFENCE_SUCCESS = 0; 101 102 /** 103 * The constant used to indicate that too many geofences have been registered. 104 */ 105 public static final int GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 1; 106 107 /** 108 * The constant used to indicate that the geofence id already exists. 109 */ 110 public static final int GEOFENCE_ERROR_ID_EXISTS = 2; 111 112 /** 113 * The constant used to indicate that the geofence id is unknown. 114 */ 115 public static final int GEOFENCE_ERROR_ID_UNKNOWN = 3; 116 117 /** 118 * The constant used to indicate that the transition requested for the geofence is invalid. 119 */ 120 public static final int GEOFENCE_ERROR_INVALID_TRANSITION = 4; 121 122 /** 123 * The constant used to indicate that the geofence operation has failed. 124 */ 125 public static final int GEOFENCE_FAILURE = 5; 126 127 /** 128 * The constant used to indicate that the operation failed due to insufficient memory. 129 */ 130 public static final int GEOFENCE_ERROR_INSUFFICIENT_MEMORY = 6; 131 132 private HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper> 133 mCallbacks = new HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>(); 134 private HashMap<GeofenceHardwareMonitorCallback, GeofenceHardwareMonitorCallbackWrapper> 135 mMonitorCallbacks = new HashMap<GeofenceHardwareMonitorCallback, 136 GeofenceHardwareMonitorCallbackWrapper>(); 137 /** 138 * @hide 139 */ 140 public GeofenceHardware(IGeofenceHardware service) { 141 mService = service; 142 } 143 144 /** 145 * Returns all the hardware geofence monitoring systems which are supported 146 * 147 * <p> Call {@link #getStatusOfMonitoringType(int)} to know the current state 148 * of a monitoring system. 149 * 150 * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access 151 * geofencing in hardware. 152 * 153 * @return An array of all the monitoring types. 154 * An array of length 0 is returned in case of errors. 155 */ 156 public int[] getMonitoringTypes() { 157 try { 158 return mService.getMonitoringTypes(); 159 } catch (RemoteException e) { 160 } 161 return new int[0]; 162 } 163 164 /** 165 * Returns current status of a hardware geofence monitoring system. 166 * 167 * <p>Status can be one of {@link #MONITOR_CURRENTLY_AVAILABLE}, 168 * {@link #MONITOR_CURRENTLY_UNAVAILABLE} or {@link #MONITOR_UNSUPPORTED} 169 * 170 * <p> Some supported hardware monitoring systems might not be available 171 * for monitoring geofences in certain scenarios. For example, when a user 172 * enters a building, the GPS hardware subsystem might not be able monitor 173 * geofences and will change from {@link #MONITOR_CURRENTLY_AVAILABLE} to 174 * {@link #MONITOR_CURRENTLY_UNAVAILABLE}. 175 * 176 * @param monitoringType 177 * @return Current status of the monitoring type. 178 */ 179 public int getStatusOfMonitoringType(int monitoringType) { 180 try { 181 return mService.getStatusOfMonitoringType(monitoringType); 182 } catch (RemoteException e) { 183 return MONITOR_UNSUPPORTED; 184 } 185 } 186 187 /** 188 * Creates a circular geofence which is monitored by subsystems in the hardware. 189 * 190 * <p> When the device detects that is has entered, exited or is uncertain 191 * about the area specified by the geofence, the given callback will be called. 192 * 193 * <p> If this call returns true, it means that the geofence has been sent to the hardware. 194 * {@link GeofenceHardwareCallback#onGeofenceAdd} will be called with the result of the 195 * add call from the hardware. The {@link GeofenceHardwareCallback#onGeofenceAdd} will be 196 * called with the following parameters when a transition event occurs. 197 * <ul> 198 * <li> The geofence Id 199 * <li> The location object indicating the last known location. 200 * <li> The transition associated with the geofence. One of 201 * {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN} 202 * <li> The timestamp when the geofence transition occured. 203 * <li> The monitoring type ({@link #MONITORING_TYPE_GPS_HARDWARE} is one such example) 204 * that was used. 205 * </ul> 206 * 207 * <p> The geofence will be monitored by the subsystem specified by monitoring_type parameter. 208 * The application does not need to hold a wakelock when the monitoring 209 * is being done by the underlying hardware subsystem. If the same geofence Id is being 210 * monitored by two different monitoring systems, the same id can be used for both calls, as 211 * long as the same callback object is used. 212 * 213 * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when 214 * {@link #MONITORING_TYPE_GPS_HARDWARE} is used. 215 * 216 * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access 217 * geofencing in hardware. 218 * 219 * <p>This API should not be called directly by the app developers. A higher level api 220 * which abstracts the hardware should be used instead. All the checks are done by the higher 221 * level public API. Any needed locking should be handled by the higher level API. 222 * 223 * <p> Create a geofence request object using the methods in {@link GeofenceHardwareRequest} to 224 * set all the characteristics of the geofence. Use the created GeofenceHardwareRequest object 225 * in this call. 226 * 227 * @param geofenceId The id associated with the geofence. 228 * @param monitoringType The type of the hardware subsystem that should be used 229 * to monitor the geofence. 230 * @param geofenceRequest The {@link GeofenceHardwareRequest} object associated with the 231 * geofence. 232 * @param callback {@link GeofenceHardwareCallback} that will be use to notify the 233 * transition. 234 * @return true when the geofence is successfully sent to the hardware for addition. 235 * @throws IllegalArgumentException when the geofence request type is not supported. 236 */ 237 public boolean addGeofence(int geofenceId, int monitoringType, GeofenceHardwareRequest 238 geofenceRequest, GeofenceHardwareCallback callback) { 239 try { 240 if (geofenceRequest.getType() == GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) { 241 return mService.addCircularFence(geofenceId, monitoringType, 242 geofenceRequest.getLatitude(), 243 geofenceRequest.getLongitude(), geofenceRequest.getRadius(), 244 geofenceRequest.getLastTransition(), 245 geofenceRequest.getMonitorTransitions(), 246 geofenceRequest.getNotificationResponsiveness(), 247 geofenceRequest.getUnknownTimer(), 248 getCallbackWrapper(callback)); 249 } else { 250 throw new IllegalArgumentException("Geofence Request type not supported"); 251 } 252 } catch (RemoteException e) { 253 } 254 return false; 255 } 256 257 /** 258 * Removes a geofence added by {@link #addGeofence} call. 259 * 260 * <p> If this call returns true, it means that the geofence has been sent to the hardware. 261 * {@link GeofenceHardwareCallback#onGeofenceRemove} will be called with the result of the 262 * remove call from the hardware. 263 * 264 * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when 265 * {@link #MONITORING_TYPE_GPS_HARDWARE} is used. 266 * 267 * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access 268 * geofencing in hardware. 269 * 270 * <p>This API should not be called directly by the app developers. A higher level api 271 * which abstracts the hardware should be used instead. All the checks are done by the higher 272 * level public API. Any needed locking should be handled by the higher level API. 273 * 274 * @param geofenceId The id of the geofence. 275 * @param monitoringType The type of the hardware subsystem that should be used 276 * to monitor the geofence. 277 * @return true when the geofence is successfully sent to the hardware for removal. . 278 */ 279 public boolean removeGeofence(int geofenceId, int monitoringType) { 280 try { 281 return mService.removeGeofence(geofenceId, monitoringType); 282 } catch (RemoteException e) { 283 } 284 return false; 285 } 286 287 /** 288 * Pauses the monitoring of a geofence added by {@link #addGeofence} call. 289 * 290 * <p> If this call returns true, it means that the geofence has been sent to the hardware. 291 * {@link GeofenceHardwareCallback#onGeofencePause} will be called with the result of the 292 * pause call from the hardware. 293 * 294 * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when 295 * {@link #MONITORING_TYPE_GPS_HARDWARE} is used. 296 * 297 * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access 298 * geofencing in hardware. 299 * 300 * <p>This API should not be called directly by the app developers. A higher level api 301 * which abstracts the hardware should be used instead. All the checks are done by the higher 302 * level public API. Any needed locking should be handled by the higher level API. 303 * 304 * @param geofenceId The id of the geofence. 305 * @param monitoringType The type of the hardware subsystem that should be used 306 * to monitor the geofence. 307 * @return true when the geofence is successfully sent to the hardware for pausing. 308 */ 309 public boolean pauseGeofence(int geofenceId, int monitoringType) { 310 try { 311 return mService.pauseGeofence(geofenceId, monitoringType); 312 } catch (RemoteException e) { 313 } 314 return false; 315 } 316 317 /** 318 * Resumes the monitoring of a geofence added by {@link #pauseGeofence} call. 319 * 320 * <p> If this call returns true, it means that the geofence has been sent to the hardware. 321 * {@link GeofenceHardwareCallback#onGeofenceResume} will be called with the result of the 322 * resume call from the hardware. 323 * 324 * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when 325 * {@link #MONITORING_TYPE_GPS_HARDWARE} is used. 326 * 327 * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access 328 * geofencing in hardware. 329 * 330 * <p>This API should not be called directly by the app developers. A higher level api 331 * which abstracts the hardware should be used instead. All the checks are done by the higher 332 * level public API. Any needed locking should be handled by the higher level API. 333 * 334 * @param geofenceId The id of the geofence. 335 * @param monitoringType The type of the hardware subsystem that should be used 336 * to monitor the geofence. 337 * @param monitorTransition Bitwise OR of {@link #GEOFENCE_ENTERED}, 338 * {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN} 339 * @return true when the geofence is successfully sent to the hardware for resumption. 340 */ 341 public boolean resumeGeofence(int geofenceId, int monitoringType, int monitorTransition) { 342 try { 343 return mService.resumeGeofence(geofenceId, monitoringType, monitorTransition); 344 } catch (RemoteException e) { 345 } 346 return false; 347 } 348 349 /** 350 * Register the callback to be notified when the state of a hardware geofence 351 * monitoring system changes. For instance, it can change from 352 * {@link #MONITOR_CURRENTLY_AVAILABLE} to {@link #MONITOR_CURRENTLY_UNAVAILABLE} 353 * 354 * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when 355 * {@link #MONITORING_TYPE_GPS_HARDWARE} is used. 356 * 357 * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access 358 * geofencing in hardware. 359 * 360 * <p>This API should not be called directly by the app developers. A higher level api 361 * which abstracts the hardware should be used instead. All the checks are done by the higher 362 * level public API. Any needed locking should be handled by the higher level API. 363 * 364 * <p> The same callback object can be used to be informed of geofence transitions 365 * and state changes of the underlying hardware subsystem. 366 * 367 * @param monitoringType Type of the monitor 368 * @param callback Callback that will be called. 369 * @return true on success 370 */ 371 public boolean registerForMonitorStateChangeCallback(int monitoringType, 372 GeofenceHardwareMonitorCallback callback) { 373 try { 374 return mService.registerForMonitorStateChangeCallback(monitoringType, 375 getMonitorCallbackWrapper(callback)); 376 } catch (RemoteException e) { 377 } 378 return false; 379 } 380 381 /** 382 * Unregister the callback that was used with {@link #registerForMonitorStateChangeCallback} 383 * to notify when the state of the hardware geofence monitoring system changes. 384 * 385 * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when 386 * {@link #MONITORING_TYPE_GPS_HARDWARE} is used. 387 * 388 * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access 389 * geofencing in hardware. 390 * 391 * <p>This API should not be called directly by the app developers. A higher level api 392 * which abstracts the hardware should be used instead. All the checks are done by the higher 393 * level public API. Any needed locking should be handled by the higher level API. 394 * 395 * @param monitoringType Type of the monitor 396 * @param callback Callback that will be called. 397 * @return true on success 398 */ 399 public boolean unregisterForMonitorStateChangeCallback(int monitoringType, 400 GeofenceHardwareMonitorCallback callback) { 401 boolean result = false; 402 try { 403 result = mService.unregisterForMonitorStateChangeCallback(monitoringType, 404 getMonitorCallbackWrapper(callback)); 405 if (result) removeMonitorCallback(callback); 406 407 } catch (RemoteException e) { 408 } 409 return result; 410 } 411 412 413 private void removeCallback(GeofenceHardwareCallback callback) { 414 synchronized (mCallbacks) { 415 mCallbacks.remove(callback); 416 } 417 } 418 419 private GeofenceHardwareCallbackWrapper getCallbackWrapper(GeofenceHardwareCallback callback) { 420 synchronized (mCallbacks) { 421 GeofenceHardwareCallbackWrapper wrapper = mCallbacks.get(callback); 422 if (wrapper == null) { 423 wrapper = new GeofenceHardwareCallbackWrapper(callback); 424 mCallbacks.put(callback, wrapper); 425 } 426 return wrapper; 427 } 428 } 429 430 private void removeMonitorCallback(GeofenceHardwareMonitorCallback callback) { 431 synchronized (mMonitorCallbacks) { 432 mMonitorCallbacks.remove(callback); 433 } 434 } 435 436 private GeofenceHardwareMonitorCallbackWrapper getMonitorCallbackWrapper( 437 GeofenceHardwareMonitorCallback callback) { 438 synchronized (mMonitorCallbacks) { 439 GeofenceHardwareMonitorCallbackWrapper wrapper = mMonitorCallbacks.get(callback); 440 if (wrapper == null) { 441 wrapper = new GeofenceHardwareMonitorCallbackWrapper(callback); 442 mMonitorCallbacks.put(callback, wrapper); 443 } 444 return wrapper; 445 } 446 } 447 448 class GeofenceHardwareMonitorCallbackWrapper extends IGeofenceHardwareMonitorCallback.Stub { 449 private WeakReference<GeofenceHardwareMonitorCallback> mCallback; 450 451 GeofenceHardwareMonitorCallbackWrapper(GeofenceHardwareMonitorCallback c) { 452 mCallback = new WeakReference<GeofenceHardwareMonitorCallback>(c); 453 } 454 455 public void onMonitoringSystemChange(int monitoringType, boolean available, 456 Location location) { 457 GeofenceHardwareMonitorCallback c = mCallback.get(); 458 if (c != null) c.onMonitoringSystemChange(monitoringType, available, location); 459 } 460 } 461 462 class GeofenceHardwareCallbackWrapper extends IGeofenceHardwareCallback.Stub { 463 private WeakReference<GeofenceHardwareCallback> mCallback; 464 465 GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c) { 466 mCallback = new WeakReference<GeofenceHardwareCallback>(c); 467 } 468 469 public void onGeofenceTransition(int geofenceId, int transition, Location location, 470 long timestamp, int monitoringType) { 471 GeofenceHardwareCallback c = mCallback.get(); 472 if (c != null) { 473 c.onGeofenceTransition(geofenceId, transition, location, timestamp, 474 monitoringType); 475 } 476 } 477 478 public void onGeofenceAdd(int geofenceId, int status) { 479 GeofenceHardwareCallback c = mCallback.get(); 480 if (c != null) c.onGeofenceAdd(geofenceId, status); 481 } 482 483 public void onGeofenceRemove(int geofenceId, int status) { 484 GeofenceHardwareCallback c = mCallback.get(); 485 if (c != null) { 486 c.onGeofenceRemove(geofenceId, status); 487 removeCallback(c); 488 } 489 } 490 491 public void onGeofencePause(int geofenceId, int status) { 492 GeofenceHardwareCallback c = mCallback.get(); 493 if (c != null) { 494 c.onGeofencePause(geofenceId, status); 495 } 496 } 497 498 public void onGeofenceResume(int geofenceId, int status) { 499 GeofenceHardwareCallback c = mCallback.get(); 500 if (c != null) c.onGeofenceResume(geofenceId, status); 501 } 502 } 503} 504