/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware.location; import android.annotation.SystemApi; import android.location.Location; import android.os.Build; import android.os.RemoteException; import java.lang.ref.WeakReference; import java.util.HashMap; /** * This class handles geofences managed by various hardware subsystems. It contains * the public APIs that is needed to accomplish the task. * *
The APIs should not be called directly by the app developers. A higher level api * which abstracts the hardware should be used instead. All the checks are done by the higher * level public API. Any needed locking should be handled by the higher level API. * *
There are 3 states associated with a Geofence: Inside, Outside, Unknown. * There are 3 transitions: {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED}, * {@link #GEOFENCE_UNCERTAIN}. The APIs only expose the transitions. * *
Inside state: The hardware subsystem is reasonably confident that the user is inside
* the geofence. Outside state: The hardware subsystem is reasonably confident that the user
* is outside the geofence Unknown state: Unknown state can be interpreted as a state in which the
* monitoring subsystem isn't confident enough that the user is either inside or
* outside the Geofence. If the accuracy does not improve for a sufficient period of time,
* the {@link #GEOFENCE_UNCERTAIN} transition would be triggered. If the accuracy improves later,
* an appropriate transition would be triggered. The "reasonably confident" parameter
* depends on the hardware system and the positioning algorithms used.
* For instance, {@link #MONITORING_TYPE_GPS_HARDWARE} uses 95% as a confidence level.
*
* @hide
*/
@SystemApi
public final class GeofenceHardware {
private IGeofenceHardware mService;
// Hardware systems that do geofence monitoring.
static final int NUM_MONITORS = 2;
/**
* Constant for geofence monitoring done by the GPS hardware.
*/
public static final int MONITORING_TYPE_GPS_HARDWARE = 0;
/**
* Constant for geofence monitoring done by the Fused hardware.
*/
public static final int MONITORING_TYPE_FUSED_HARDWARE = 1;
/**
* Constant to indicate that the monitoring system is currently
* available for monitoring geofences.
*/
public static final int MONITOR_CURRENTLY_AVAILABLE = 0;
/**
* Constant to indicate that the monitoring system is currently
* unavailable for monitoring geofences.
*/
public static final int MONITOR_CURRENTLY_UNAVAILABLE = 1;
/**
* Constant to indicate that the monitoring system is unsupported
* for hardware geofence monitoring.
*/
public static final int MONITOR_UNSUPPORTED = 2;
// The following constants need to match geofence flags in gps.h and fused_location.h
/**
* The constant to indicate that the user has entered the geofence.
*/
public static final int GEOFENCE_ENTERED = 1<<0L;
/**
* The constant to indicate that the user has exited the geofence.
*/
public static final int GEOFENCE_EXITED = 1<<1L;
/**
* The constant to indicate that the user is uncertain with respect to a
* geofence.
*/
public static final int GEOFENCE_UNCERTAIN = 1<<2L;
/**
* The constant used to indicate success of the particular geofence call
*/
public static final int GEOFENCE_SUCCESS = 0;
/**
* The constant used to indicate that too many geofences have been registered.
*/
public static final int GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 1;
/**
* The constant used to indicate that the geofence id already exists.
*/
public static final int GEOFENCE_ERROR_ID_EXISTS = 2;
/**
* The constant used to indicate that the geofence id is unknown.
*/
public static final int GEOFENCE_ERROR_ID_UNKNOWN = 3;
/**
* The constant used to indicate that the transition requested for the geofence is invalid.
*/
public static final int GEOFENCE_ERROR_INVALID_TRANSITION = 4;
/**
* The constant used to indicate that the geofence operation has failed.
*/
public static final int GEOFENCE_FAILURE = 5;
/**
* The constant used to indicate that the operation failed due to insufficient memory.
*/
public static final int GEOFENCE_ERROR_INSUFFICIENT_MEMORY = 6;
// the following values must match the definitions in fused_location.h
/**
* The constant used to indicate that the monitoring system supports GNSS.
*/
public static final int SOURCE_TECHNOLOGY_GNSS = (1<<0);
/**
* The constant used to indicate that the monitoring system supports WiFi.
*/
public static final int SOURCE_TECHNOLOGY_WIFI = (1<<1);
/**
* The constant used to indicate that the monitoring system supports Sensors.
*/
public static final int SOURCE_TECHNOLOGY_SENSORS = (1<<2);
/**
* The constant used to indicate that the monitoring system supports Cell.
*/
public static final int SOURCE_TECHNOLOGY_CELL = (1<<3);
/**
* The constant used to indicate that the monitoring system supports Bluetooth.
*/
public static final int SOURCE_TECHNOLOGY_BLUETOOTH = (1<<4);
private HashMap Call {@link #getStatusOfMonitoringType(int)} to know the current state
* of a monitoring system.
*
* Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
* geofencing in hardware.
*
* @return An array of all the monitoring types.
* An array of length 0 is returned in case of errors.
*/
public int[] getMonitoringTypes() {
try {
return mService.getMonitoringTypes();
} catch (RemoteException e) {
}
return new int[0];
}
/**
* Returns current status of a hardware geofence monitoring system.
*
* Status can be one of {@link #MONITOR_CURRENTLY_AVAILABLE},
* {@link #MONITOR_CURRENTLY_UNAVAILABLE} or {@link #MONITOR_UNSUPPORTED}
*
* Some supported hardware monitoring systems might not be available
* for monitoring geofences in certain scenarios. For example, when a user
* enters a building, the GPS hardware subsystem might not be able monitor
* geofences and will change from {@link #MONITOR_CURRENTLY_AVAILABLE} to
* {@link #MONITOR_CURRENTLY_UNAVAILABLE}.
*
* @param monitoringType
* @return Current status of the monitoring type.
*/
public int getStatusOfMonitoringType(int monitoringType) {
try {
return mService.getStatusOfMonitoringType(monitoringType);
} catch (RemoteException e) {
return MONITOR_UNSUPPORTED;
}
}
/**
* Creates a circular geofence which is monitored by subsystems in the hardware.
*
* When the device detects that is has entered, exited or is uncertain
* about the area specified by the geofence, the given callback will be called.
*
* If this call returns true, it means that the geofence has been sent to the hardware.
* {@link GeofenceHardwareCallback#onGeofenceAdd} will be called with the result of the
* add call from the hardware. The {@link GeofenceHardwareCallback#onGeofenceAdd} will be
* called with the following parameters when a transition event occurs.
* The geofence will be monitored by the subsystem specified by monitoring_type parameter.
* The application does not need to hold a wakelock when the monitoring
* is being done by the underlying hardware subsystem. If the same geofence Id is being
* monitored by two different monitoring systems, the same id can be used for both calls, as
* long as the same callback object is used.
*
* Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
* {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
*
* Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
* geofencing in hardware.
*
* This API should not be called directly by the app developers. A higher level api
* which abstracts the hardware should be used instead. All the checks are done by the higher
* level public API. Any needed locking should be handled by the higher level API.
*
* Create a geofence request object using the methods in {@link GeofenceHardwareRequest} to
* set all the characteristics of the geofence. Use the created GeofenceHardwareRequest object
* in this call.
*
* @param geofenceId The id associated with the geofence.
* @param monitoringType The type of the hardware subsystem that should be used
* to monitor the geofence.
* @param geofenceRequest The {@link GeofenceHardwareRequest} object associated with the
* geofence.
* @param callback {@link GeofenceHardwareCallback} that will be use to notify the
* transition.
* @return true when the geofence is successfully sent to the hardware for addition.
* @throws IllegalArgumentException when the geofence request type is not supported.
*/
public boolean addGeofence(int geofenceId, int monitoringType, GeofenceHardwareRequest
geofenceRequest, GeofenceHardwareCallback callback) {
try {
if (geofenceRequest.getType() == GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) {
return mService.addCircularFence(
monitoringType,
new GeofenceHardwareRequestParcelable(geofenceId, geofenceRequest),
getCallbackWrapper(callback));
} else {
throw new IllegalArgumentException("Geofence Request type not supported");
}
} catch (RemoteException e) {
}
return false;
}
/**
* Removes a geofence added by {@link #addGeofence} call.
*
* If this call returns true, it means that the geofence has been sent to the hardware.
* {@link GeofenceHardwareCallback#onGeofenceRemove} will be called with the result of the
* remove call from the hardware.
*
* Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
* {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
*
* Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
* geofencing in hardware.
*
* This API should not be called directly by the app developers. A higher level api
* which abstracts the hardware should be used instead. All the checks are done by the higher
* level public API. Any needed locking should be handled by the higher level API.
*
* @param geofenceId The id of the geofence.
* @param monitoringType The type of the hardware subsystem that should be used
* to monitor the geofence.
* @return true when the geofence is successfully sent to the hardware for removal. .
*/
public boolean removeGeofence(int geofenceId, int monitoringType) {
try {
return mService.removeGeofence(geofenceId, monitoringType);
} catch (RemoteException e) {
}
return false;
}
/**
* Pauses the monitoring of a geofence added by {@link #addGeofence} call.
*
* If this call returns true, it means that the geofence has been sent to the hardware.
* {@link GeofenceHardwareCallback#onGeofencePause} will be called with the result of the
* pause call from the hardware.
*
* Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
* {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
*
* Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
* geofencing in hardware.
*
* This API should not be called directly by the app developers. A higher level api
* which abstracts the hardware should be used instead. All the checks are done by the higher
* level public API. Any needed locking should be handled by the higher level API.
*
* @param geofenceId The id of the geofence.
* @param monitoringType The type of the hardware subsystem that should be used
* to monitor the geofence.
* @return true when the geofence is successfully sent to the hardware for pausing.
*/
public boolean pauseGeofence(int geofenceId, int monitoringType) {
try {
return mService.pauseGeofence(geofenceId, monitoringType);
} catch (RemoteException e) {
}
return false;
}
/**
* Resumes the monitoring of a geofence added by {@link #pauseGeofence} call.
*
* If this call returns true, it means that the geofence has been sent to the hardware.
* {@link GeofenceHardwareCallback#onGeofenceResume} will be called with the result of the
* resume call from the hardware.
*
* Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
* {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
*
* Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
* geofencing in hardware.
*
* This API should not be called directly by the app developers. A higher level api
* which abstracts the hardware should be used instead. All the checks are done by the higher
* level public API. Any needed locking should be handled by the higher level API.
*
* @param geofenceId The id of the geofence.
* @param monitoringType The type of the hardware subsystem that should be used
* to monitor the geofence.
* @param monitorTransition Bitwise OR of {@link #GEOFENCE_ENTERED},
* {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
* @return true when the geofence is successfully sent to the hardware for resumption.
*/
public boolean resumeGeofence(int geofenceId, int monitoringType, int monitorTransition) {
try {
return mService.resumeGeofence(geofenceId, monitoringType, monitorTransition);
} catch (RemoteException e) {
}
return false;
}
/**
* Register the callback to be notified when the state of a hardware geofence
* monitoring system changes. For instance, it can change from
* {@link #MONITOR_CURRENTLY_AVAILABLE} to {@link #MONITOR_CURRENTLY_UNAVAILABLE}
*
* Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
* {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
*
* Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
* geofencing in hardware.
*
* This API should not be called directly by the app developers. A higher level api
* which abstracts the hardware should be used instead. All the checks are done by the higher
* level public API. Any needed locking should be handled by the higher level API.
*
* The same callback object can be used to be informed of geofence transitions
* and state changes of the underlying hardware subsystem.
*
* @param monitoringType Type of the monitor
* @param callback Callback that will be called.
* @return true on success
*/
public boolean registerForMonitorStateChangeCallback(int monitoringType,
GeofenceHardwareMonitorCallback callback) {
try {
return mService.registerForMonitorStateChangeCallback(monitoringType,
getMonitorCallbackWrapper(callback));
} catch (RemoteException e) {
}
return false;
}
/**
* Unregister the callback that was used with {@link #registerForMonitorStateChangeCallback}
* to notify when the state of the hardware geofence monitoring system changes.
*
* Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
* {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
*
* Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
* geofencing in hardware.
*
* This API should not be called directly by the app developers. A higher level api
* which abstracts the hardware should be used instead. All the checks are done by the higher
* level public API. Any needed locking should be handled by the higher level API.
*
* @param monitoringType Type of the monitor
* @param callback Callback that will be called.
* @return true on success
*/
public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
GeofenceHardwareMonitorCallback callback) {
boolean result = false;
try {
result = mService.unregisterForMonitorStateChangeCallback(monitoringType,
getMonitorCallbackWrapper(callback));
if (result) removeMonitorCallback(callback);
} catch (RemoteException e) {
}
return result;
}
private void removeCallback(GeofenceHardwareCallback callback) {
synchronized (mCallbacks) {
mCallbacks.remove(callback);
}
}
private GeofenceHardwareCallbackWrapper getCallbackWrapper(GeofenceHardwareCallback callback) {
synchronized (mCallbacks) {
GeofenceHardwareCallbackWrapper wrapper = mCallbacks.get(callback);
if (wrapper == null) {
wrapper = new GeofenceHardwareCallbackWrapper(callback);
mCallbacks.put(callback, wrapper);
}
return wrapper;
}
}
private void removeMonitorCallback(GeofenceHardwareMonitorCallback callback) {
synchronized (mMonitorCallbacks) {
mMonitorCallbacks.remove(callback);
}
}
private GeofenceHardwareMonitorCallbackWrapper getMonitorCallbackWrapper(
GeofenceHardwareMonitorCallback callback) {
synchronized (mMonitorCallbacks) {
GeofenceHardwareMonitorCallbackWrapper wrapper = mMonitorCallbacks.get(callback);
if (wrapper == null) {
wrapper = new GeofenceHardwareMonitorCallbackWrapper(callback);
mMonitorCallbacks.put(callback, wrapper);
}
return wrapper;
}
}
class GeofenceHardwareMonitorCallbackWrapper extends IGeofenceHardwareMonitorCallback.Stub {
private WeakReference
*
*
*