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