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