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