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