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