am 7228e1ae: am 8ce470dd: GPS Hardware geofencing.
* commit '7228e1aeb9a2af8fad410749e2adc61266b2649a':
GPS Hardware geofencing.
diff --git a/Android.mk b/Android.mk
index bd2bd73..8d64a93 100644
--- a/Android.mk
+++ b/Android.mk
@@ -124,6 +124,8 @@
core/java/android/hardware/display/IDisplayManagerCallback.aidl \
core/java/android/hardware/input/IInputManager.aidl \
core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
+ core/java/android/hardware/location/IGeofenceHardware.aidl \
+ core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \
core/java/android/hardware/usb/IUsbManager.aidl \
core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \
@@ -210,10 +212,12 @@
location/java/android/location/ICountryDetector.aidl \
location/java/android/location/ICountryListener.aidl \
location/java/android/location/IGeocodeProvider.aidl \
+ location/java/android/location/IGeofenceProvider.aidl \
location/java/android/location/IGpsStatusListener.aidl \
location/java/android/location/IGpsStatusProvider.aidl \
location/java/android/location/ILocationListener.aidl \
location/java/android/location/ILocationManager.aidl \
+ location/java/android/location/IGpsGeofenceHardware.aidl \
location/java/android/location/INetInitiatedListener.aidl \
location/java/com/android/internal/location/ILocationProvider.aidl \
media/java/android/media/IAudioService.aidl \
diff --git a/api/current.txt b/api/current.txt
index 7a552db..8695897 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -69,6 +69,7 @@
field public static final java.lang.String INTERNAL_SYSTEM_WINDOW = "android.permission.INTERNAL_SYSTEM_WINDOW";
field public static final java.lang.String INTERNET = "android.permission.INTERNET";
field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
+ field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_ACCOUNTS = "android.permission.MANAGE_ACCOUNTS";
field public static final java.lang.String MANAGE_APP_TOKENS = "android.permission.MANAGE_APP_TOKENS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
@@ -10445,6 +10446,43 @@
}
+package android.hardware.location {
+
+ public final class GeofenceHardware {
+ method public boolean addCircularFence(int, double, double, double, int, int, int, int, int, android.hardware.location.GeofenceHardwareCallback);
+ method public int[] getMonitoringTypesAndStatus();
+ method public boolean pauseGeofence(int, int);
+ method public boolean registerForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareCallback);
+ method public boolean removeGeofence(int, int);
+ method public boolean resumeGeofence(int, int, int);
+ method public boolean unregisterForMonitorStateChangeCallback(int, android.hardware.location.GeofenceHardwareCallback);
+ field public static final int GEOFENCE_ENTERED = 1; // 0x1
+ field public static final int GEOFENCE_ERROR_ID_EXISTS = 2; // 0x2
+ field public static final int GEOFENCE_ERROR_ID_UNKNOWN = 3; // 0x3
+ field public static final int GEOFENCE_ERROR_INVALID_TRANSITION = 4; // 0x4
+ field public static final int GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 1; // 0x1
+ field public static final int GEOFENCE_EXITED = 2; // 0x2
+ field public static final int GEOFENCE_FAILURE = 5; // 0x5
+ field public static final int GEOFENCE_SUCCESS = 0; // 0x0
+ field public static final int GEOFENCE_UNCERTAIN = 4; // 0x4
+ field public static final int MONITORING_TYPE_GPS_HARDWARE = 0; // 0x0
+ field public static final int MONITOR_CURRENTLY_AVAILABLE = 0; // 0x0
+ field public static final int MONITOR_CURRENTLY_UNAVAILABLE = 1; // 0x1
+ field public static final int MONITOR_UNSUPPORTED = 2; // 0x2
+ }
+
+ public abstract class GeofenceHardwareCallback {
+ ctor public GeofenceHardwareCallback();
+ method public void onGeofenceAdd(int, int);
+ method public void onGeofenceChange(int, int, android.location.Location, long, int);
+ method public void onGeofencePause(int, int);
+ method public void onGeofenceRemove(int, int);
+ method public void onGeofenceResume(int, int);
+ method public void onMonitoringSystemChange(int, boolean, android.location.Location);
+ }
+
+}
+
package android.hardware.usb {
public class UsbAccessory implements android.os.Parcelable {
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
new file mode 100644
index 0000000..35bbb9c
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -0,0 +1,439 @@
+/*
+ * 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.content.Context;
+import android.location.Location;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * This class handles geofences managed by various hardware subsystems. It contains
+ * the public APIs that is needed to accomplish the task.
+ *
+ * <p>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.
+ *
+ * <p> 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.
+ *
+ * <p> 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.
+ */
+public final class GeofenceHardware {
+ private IGeofenceHardware mService;
+
+ // Hardware systems that do geofence monitoring.
+ static final int NUM_MONITORS = 1;
+
+ /**
+ * Constant for geofence monitoring done by the GPS hardware.
+ */
+ public static final int MONITORING_TYPE_GPS_HARDWARE = 0;
+
+ /**
+ * Constant to indiciate that the monitoring system is currently
+ * available for monitoring geofences.
+ */
+ public static final int MONITOR_CURRENTLY_AVAILABLE = 0;
+
+ /**
+ * Constant to indiciate that the monitoring system is currently
+ * unavailable for monitoring geofences.
+ */
+ public static final int MONITOR_CURRENTLY_UNAVAILABLE = 1;
+
+ /**
+ * Constant to indiciate 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
+ /**
+ * 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. nn
+ */
+ 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;
+
+ static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
+ static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
+
+ private HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>
+ mCallbacks = new HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>();
+ /**
+ * @hide
+ */
+ public GeofenceHardware(IGeofenceHardware service) {
+ mService = service;
+ }
+
+ /**
+ * Returns all the hardware geofence monitoring systems and their status.
+ * Status can be one of {@link #MONITOR_CURRENTLY_AVAILABLE},
+ * {@link #MONITOR_CURRENTLY_UNAVAILABLE} or {@link #MONITOR_UNSUPPORTED}
+ *
+ * <p> 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}.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * @return An array indexed by the various monitoring types and their status.
+ * An array of length 0 is returned in case of errors.
+ */
+ public int[] getMonitoringTypesAndStatus() {
+ try {
+ return mService.getMonitoringTypesAndStatus();
+ } catch (RemoteException e) {
+ }
+ return new int[0];
+ }
+
+ /**
+ * Creates a circular geofence which is monitored by subsystems in the hardware.
+ *
+ * <p> 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.
+ *
+ * <p> The {@link GeofenceHardwareCallback#onGeofenceChange} callback will be called,
+ * with the following parameters
+ * <ul>
+ * <li> The geofence Id
+ * <li> The location object indicating the last known location.
+ * <li> The transition associated with the geofence. One of
+ * {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
+ * <li> The timestamp when the geofence transition occured.
+ * <li> The monitoring type ({@link #MONITORING_TYPE_GPS_HARDWARE} is one such example)
+ * that was used.
+ * </ul>
+ *
+ * <p> 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.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>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 latitude Latitude of the area to be monitored.
+ * @param longitude Longitude of the area to be monitored.
+ * @param radius Radius (in meters) of the area to be monitored.
+ * @param lastTransition The current state of the geofence. Can be one of
+ * {@link #GEOFENCE_ENTERED}, {@link #GEOFENCE_EXITED},
+ * {@link #GEOFENCE_UNCERTAIN}.
+ * @param monitorTransitions Bitwise OR of {@link #GEOFENCE_ENTERED},
+ * {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
+ * @param notificationResponsivenes Defines the best-effort description
+ * of how soon should the callback be called when the transition
+ * associated with the Geofence is triggered. For instance, if
+ * set to 1000 millseconds with {@link #GEOFENCE_ENTERED},
+ * the callback will be called 1000 milliseconds within entering
+ * the geofence. This parameter is defined in milliseconds.
+ * @param unknownTimer The time limit after which the
+ * {@link #GEOFENCE_UNCERTAIN} transition
+ * should be triggered. This paramter is defined in milliseconds.
+ * @param monitoringType The type of the hardware subsystem that should be used
+ * to monitor the geofence.
+ * @param callback {@link GeofenceHardwareCallback} that will be use to notify the
+ * transition.
+ * @return true on success.
+ */
+ public boolean addCircularFence(int geofenceId, double latitude, double longitude,
+ double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes,
+ int unknownTimer, int monitoringType, GeofenceHardwareCallback callback) {
+ try {
+ return mService.addCircularFence(geofenceId, latitude, longitude, radius,
+ lastTransition, monitorTransitions, notificationResponsivenes, unknownTimer,
+ monitoringType, getCallbackWrapper(callback));
+ } catch (RemoteException e) {
+ }
+ return false;
+ }
+
+ /**
+ * Removes a geofence added by {@link #addCircularFence} call.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>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 on success.
+ */
+ 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 #addCircularFence} call.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>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 on success.
+ */
+ 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.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>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 monitorTransition Bitwise OR of {@link #GEOFENCE_ENTERED},
+ * {@link #GEOFENCE_EXITED}, {@link #GEOFENCE_UNCERTAIN}
+ * @param monitoringType The type of the hardware subsystem that should be used
+ * to monitor the geofence.
+ * @return true on success.
+ */
+ public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) {
+ try {
+ return mService.resumeGeofence(geofenceId, monitorTransition, monitoringType);
+ } 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}
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>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.
+ *
+ * <p> 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,
+ GeofenceHardwareCallback callback) {
+ try {
+ return mService.registerForMonitorStateChangeCallback(monitoringType,
+ getCallbackWrapper(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.
+ *
+ * <p> Requires {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission when
+ * {@link #MONITORING_TYPE_GPS_HARDWARE} is used.
+ *
+ * <p> Requires {@link android.Manifest.permission#LOCATION_HARDWARE} permission to access
+ * geofencing in hardware.
+ *
+ * <p>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,
+ GeofenceHardwareCallback callback) {
+ boolean result = false;
+ try {
+ result = mService.unregisterForMonitorStateChangeCallback(monitoringType,
+ getCallbackWrapper(callback));
+ if (result) removeCallback(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;
+ }
+ }
+
+ class GeofenceHardwareCallbackWrapper extends IGeofenceHardwareCallback.Stub {
+ private WeakReference<GeofenceHardwareCallback> mCallback;
+
+ GeofenceHardwareCallbackWrapper(GeofenceHardwareCallback c) {
+ mCallback = new WeakReference<GeofenceHardwareCallback>(c);
+ }
+
+ public void onMonitoringSystemChange(int monitoringType, boolean available,
+ Location location) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onMonitoringSystemChange(monitoringType, available, location);
+ }
+
+ public void onGeofenceChange(int geofenceId, int transition, Location location,
+ long timestamp, int monitoringType) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) {
+ c.onGeofenceChange(geofenceId, transition, location, timestamp,
+ monitoringType);
+ }
+ }
+
+ public void onGeofenceAdd(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onGeofenceAdd(geofenceId, status);
+ }
+
+ public void onGeofenceRemove(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) {
+ c.onGeofenceRemove(geofenceId, status);
+ removeCallback(c);
+ }
+ }
+
+ public void onGeofencePause(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onGeofencePause(geofenceId, status);
+ }
+
+ public void onGeofenceResume(int geofenceId, int status) {
+ GeofenceHardwareCallback c = mCallback.get();
+ if (c != null) c.onGeofenceResume(geofenceId, status);
+ }
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareCallback.java b/core/java/android/hardware/location/GeofenceHardwareCallback.java
new file mode 100644
index 0000000..8ab582a
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareCallback.java
@@ -0,0 +1,100 @@
+/*
+ * 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.location.Location;
+
+/**
+ * The callback class associated with the APIs in {@link GeofenceHardware}
+ */
+public abstract class GeofenceHardwareCallback {
+
+ /**
+ * The callback called when the state of a monitoring system changes.
+ * {@link GeofenceHardware#MONITORING_TYPE_GPS_HARDWARE} is an example of a
+ * monitoring system.
+ *
+ * @param monitoringType The type of the monitoring system.
+ * @param available Indicates whether the system is currently available or not.
+ * @param location The last known location according to the monitoring system.
+ */
+ public void onMonitoringSystemChange(int monitoringType, boolean available, Location location) {
+ }
+
+ /**
+ * The callback called when there is a transition to report for the specific
+ * geofence.
+ *
+ * @param geofenceId The geofence ID of the geofence
+ * @param transition One of {@link GeofenceHardware#GEOFENCE_ENTERED},
+ * {@link GeofenceHardware#GEOFENCE_EXITED}, {@link GeofenceHardware#GEOFENCE_UNCERTAIN}
+ * @param location The last known location according to the monitoring system.
+ * @param timestamp The timestamp (elapsed real time in milliseconds) when the transition was
+ * detected
+ * @param monitoringType Type of the monitoring system.
+ */
+ public void onGeofenceChange(int geofenceId, int transition, Location location,
+ long timestamp, int monitoringType) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the add call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_EXISTS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_INVALID_TRANSITION},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_TOO_MANY_GEOFENCES},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofenceAdd(int geofenceId, int status) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the remove call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_UNKNOWN},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofenceRemove(int geofenceId, int status) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the pause call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_UNKNOWN},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofencePause(int geofenceId, int status) {
+ }
+
+ /**
+ * The callback called to notify the success or failure of the resume call.
+ *
+ * @param geofenceId The ID of the geofence.
+ * @param status One of {@link GeofenceHardware#GEOFENCE_SUCCESS},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_ID_UNKNOWN},
+ * {@link GeofenceHardware#GEOFENCE_ERROR_INVALID_TRANSITION},
+ * {@link GeofenceHardware#GEOFENCE_FAILURE}
+ */
+ public void onGeofenceResume(int geofenceId, int status) {
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
new file mode 100644
index 0000000..21f1ea6
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -0,0 +1,599 @@
+/*
+ * 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.content.Context;
+import android.content.pm.PackageManager;
+import android.location.IGpsGeofenceHardware;
+import android.location.Location;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class manages the geofences which are handled by hardware.
+ *
+ * @hide
+ */
+public final class GeofenceHardwareImpl {
+ private static final String TAG = "GeofenceHardwareImpl";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Context mContext;
+ private static GeofenceHardwareImpl sInstance;
+ private PowerManager.WakeLock mWakeLock;
+ private SparseArray<IGeofenceHardwareCallback> mGeofences =
+ new SparseArray<IGeofenceHardwareCallback>();
+ private ArrayList<IGeofenceHardwareCallback>[] mCallbacks =
+ new ArrayList[GeofenceHardware.NUM_MONITORS];
+
+ private IGpsGeofenceHardware mGpsService;
+
+ private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS];
+
+ // mGeofenceHandler message types
+ private static final int GEOFENCE_TRANSITION_CALLBACK = 1;
+ private static final int ADD_GEOFENCE_CALLBACK = 2;
+ private static final int REMOVE_GEOFENCE_CALLBACK = 3;
+ private static final int PAUSE_GEOFENCE_CALLBACK = 4;
+ private static final int RESUME_GEOFENCE_CALLBACK = 5;
+ private static final int ADD_GEOFENCE = 6;
+ private static final int REMOVE_GEOFENCE = 7;
+
+ // mCallbacksHandler message types
+ private static final int GPS_GEOFENCE_STATUS = 1;
+ private static final int CALLBACK_ADD = 2;
+ private static final int CALLBACK_REMOVE = 3;
+
+ // The following constants need to match GpsLocationFlags enum in gps.h
+ private static final int LOCATION_INVALID = 0;
+ private static final int LOCATION_HAS_LAT_LONG = 1;
+ private static final int LOCATION_HAS_ALTITUDE = 2;
+ private static final int LOCATION_HAS_SPEED = 4;
+ private static final int LOCATION_HAS_BEARING = 8;
+ private static final int LOCATION_HAS_ACCURACY = 16;
+
+ // Resolution level constants used for permission checks.
+ // These constants must be in increasing order of finer resolution.
+ private static final int RESOLUTION_LEVEL_NONE = 1;
+ private static final int RESOLUTION_LEVEL_COARSE = 2;
+ private static final int RESOLUTION_LEVEL_FINE = 3;
+
+ // GPS Geofence errors. Should match gps.h constants.
+ private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0;
+ private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100;
+ private static final int GPS_GEOFENCE_ERROR_ID_EXISTS = -101;
+ private static final int GPS_GEOFENCE_ERROR_ID_UNKNOWN = -102;
+ private static final int GPS_GEOFENCE_ERROR_INVALID_TRANSITION = -103;
+ private static final int GPS_GEOFENCE_ERROR_GENERIC = -149;
+
+
+
+ public synchronized static GeofenceHardwareImpl getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new GeofenceHardwareImpl(context);
+ }
+ return sInstance;
+ }
+
+ private GeofenceHardwareImpl(Context context) {
+ mContext = context;
+ // Init everything to unsupported.
+ setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+ GeofenceHardware.MONITOR_UNSUPPORTED);
+
+ }
+
+ private void acquireWakeLock() {
+ if (mWakeLock == null) {
+ PowerManager powerManager =
+ (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ }
+ mWakeLock.acquire();
+ }
+
+ private void releaseWakeLock() {
+ if (mWakeLock.isHeld()) mWakeLock.release();
+ }
+
+ private void updateGpsHardwareAvailability() {
+ //Check which monitors are available.
+ boolean gpsSupported;
+ try {
+ gpsSupported = mGpsService.isHardwareGeofenceSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote Exception calling LocationManagerService");
+ gpsSupported = false;
+ }
+
+ if (gpsSupported) {
+ // Its assumed currently available at startup.
+ // native layer will update later.
+ setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+ GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE);
+ }
+ }
+
+ public void setGpsHardwareGeofence(IGpsGeofenceHardware service) {
+ if (mGpsService == null) {
+ mGpsService = service;
+ updateGpsHardwareAvailability();
+ } else if (service == null) {
+ mGpsService = null;
+ Log.w(TAG, "GPS Geofence Hardware service seems to have crashed");
+ } else {
+ Log.e(TAG, "Error: GpsService being set again.");
+ }
+ }
+
+ public int[] getMonitoringTypesAndStatus() {
+ synchronized (mSupportedMonitorTypes) {
+ return mSupportedMonitorTypes;
+ }
+ }
+
+ public boolean addCircularFence(int geofenceId, double latitude, double longitude,
+ double radius, int lastTransition,int monitorTransitions, int notificationResponsivenes,
+ int unknownTimer, int monitoringType, IGeofenceHardwareCallback callback) {
+ // This API is not thread safe. Operations on the same geofence need to be serialized
+ // by upper layers
+ if (DEBUG) {
+ Log.d(TAG, "addCircularFence: GeofenceId: " + geofenceId + "Latitude: " + latitude +
+ "Longitude: " + longitude + "Radius: " + radius + "LastTransition: "
+ + lastTransition + "MonitorTransition: " + monitorTransitions +
+ "NotificationResponsiveness: " + notificationResponsivenes +
+ "UnKnown Timer: " + unknownTimer + "MonitoringType: " + monitoringType);
+
+ }
+ boolean result;
+ Message m = mGeofenceHandler.obtainMessage(ADD_GEOFENCE, callback);
+ m.arg1 = geofenceId;
+ mGeofenceHandler.sendMessage(m);
+
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.addCircularHardwareGeofence(geofenceId, latitude,
+ longitude, radius, lastTransition, monitorTransitions,
+ notificationResponsivenes, unknownTimer);
+ } catch (RemoteException e) {
+ Log.e(TAG, "AddGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (!result) {
+ m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE);
+ m.arg1 = geofenceId;
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ if (DEBUG) Log.d(TAG, "addCircularFence: Result is: " + result);
+ return result;
+ }
+
+ public boolean removeGeofence(int geofenceId, int monitoringType) {
+ // This API is not thread safe. Operations on the same geofence need to be serialized
+ // by upper layers
+ if (DEBUG) Log.d(TAG, "Remove Geofence: GeofenceId: " + geofenceId);
+ boolean result = false;
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.removeHardwareGeofence(geofenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoveGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (DEBUG) Log.d(TAG, "removeGeofence: Result is: " + result);
+ return result;
+ }
+
+ public boolean pauseGeofence(int geofenceId, int monitoringType) {
+ // This API is not thread safe. Operations on the same geofence need to be serialized
+ // by upper layers
+ if (DEBUG) Log.d(TAG, "Pause Geofence: GeofenceId: " + geofenceId);
+ boolean result;
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.pauseHardwareGeofence(geofenceId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "PauseGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (DEBUG) Log.d(TAG, "pauseGeofence: Result is: " + result);
+ return result;
+ }
+
+
+ public boolean resumeGeofence(int geofenceId, int monitorTransition, int monitoringType) {
+ // This API is not thread safe. Operations on the same geofence need to be serialized
+ // by upper layers
+ if (DEBUG) Log.d(TAG, "Resume Geofence: GeofenceId: " + geofenceId);
+ boolean result;
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ if (mGpsService == null) return false;
+ try {
+ result = mGpsService.resumeHardwareGeofence(geofenceId, monitorTransition);
+ } catch (RemoteException e) {
+ Log.e(TAG, "ResumeGeofence: Remote Exception calling LocationManagerService");
+ result = false;
+ }
+ break;
+ default:
+ result = false;
+ }
+ if (DEBUG) Log.d(TAG, "resumeGeofence: Result is: " + result);
+ return result;
+ }
+
+ public boolean registerForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ Message m = mCallbacksHandler.obtainMessage(CALLBACK_ADD, callback);
+ m.arg1 = monitoringType;
+ mCallbacksHandler.sendMessage(m);
+ return true;
+ }
+
+ public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ Message m = mCallbacksHandler.obtainMessage(CALLBACK_REMOVE, callback);
+ m.arg1 = monitoringType;
+ mCallbacksHandler.sendMessage(m);
+ return true;
+ }
+
+ private Location getLocation(int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp) {
+ if (DEBUG) Log.d(TAG, "GetLocation: " + flags + ":" + latitude);
+ Location location = new Location(LocationManager.GPS_PROVIDER);
+ if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ location.setLatitude(latitude);
+ location.setLongitude(longitude);
+ location.setTime(timestamp);
+ // It would be nice to push the elapsed real-time timestamp
+ // further down the stack, but this is still useful
+ location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+ }
+ if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
+ location.setAltitude(altitude);
+ } else {
+ location.removeAltitude();
+ }
+ if ((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
+ location.setSpeed(speed);
+ } else {
+ location.removeSpeed();
+ }
+ if ((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
+ location.setBearing(bearing);
+ } else {
+ location.removeBearing();
+ }
+ if ((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
+ location.setAccuracy(accuracy);
+ } else {
+ location.removeAccuracy();
+ }
+ return location;
+ }
+
+ /**
+ * called from GpsLocationProvider to report geofence transition
+ */
+ public void reportGpsGeofenceTransition(int geofenceId, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp, int transition, long transitionTimestamp) {
+ if (DEBUG) Log.d(TAG, "GeofenceTransition: Flags: " + flags + " Lat: " + latitude +
+ " Long: " + longitude + " Altitude: " + altitude + " Speed: " + speed + " Bearing: " +
+ bearing + " Accuracy: " + accuracy + " Timestamp: " + timestamp + " Transition: " +
+ transition + " TransitionTimestamp: " + transitionTimestamp);
+ Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing,
+ accuracy, timestamp);
+ GeofenceTransition t = new GeofenceTransition(geofenceId, transition, timestamp, location);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(GEOFENCE_TRANSITION_CALLBACK, t);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider to report GPS status change.
+ */
+ public void reportGpsGeofenceStatus(int status, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp) {
+ Location location = getLocation(flags, latitude, longitude, altitude, speed, bearing,
+ accuracy, timestamp);
+ boolean available = false;
+ if (status == GeofenceHardware.GPS_GEOFENCE_AVAILABLE) available = true;
+
+ int val = (available ? GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE :
+ GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE);
+ setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, val);
+
+ acquireWakeLock();
+ Message m = mCallbacksHandler.obtainMessage(GPS_GEOFENCE_STATUS, location);
+ m.arg1 = val;
+ mCallbacksHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider add geofence callback.
+ */
+ public void reportGpsGeofenceAddStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Add Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(ADD_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider remove geofence callback.
+ */
+ public void reportGpsGeofenceRemoveStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Remove Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(REMOVE_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider pause geofence callback.
+ */
+ public void reportGpsGeofencePauseStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Pause Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(PAUSE_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ /**
+ * called from GpsLocationProvider resume geofence callback.
+ */
+ public void reportGpsGeofenceResumeStatus(int geofenceId, int status) {
+ if (DEBUG) Log.d(TAG, "Resume Callback: GPS : Id: " + geofenceId + " Status: " + status);
+ acquireWakeLock();
+ Message m = mGeofenceHandler.obtainMessage(RESUME_GEOFENCE_CALLBACK);
+ m.arg1 = geofenceId;
+ m.arg2 = getGeofenceStatus(status);
+ mGeofenceHandler.sendMessage(m);
+ }
+
+ // All operations on mGeofences
+ private Handler mGeofenceHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ int geofenceId;
+ int status;
+ IGeofenceHardwareCallback callback;
+ switch (msg.what) {
+ case ADD_GEOFENCE:
+ geofenceId = msg.arg1;
+ callback = (IGeofenceHardwareCallback) msg.obj;
+ mGeofences.put(geofenceId, callback);
+ break;
+ case REMOVE_GEOFENCE:
+ geofenceId = msg.arg1;
+ mGeofences.remove(geofenceId);
+ break;
+ case ADD_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofenceAdd(geofenceId, msg.arg2);
+ } catch (RemoteException e) {Log.i(TAG, "Remote Exception:" + e);}
+ releaseWakeLock();
+ break;
+ case REMOVE_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofenceRemove(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ mGeofences.remove(geofenceId);
+ releaseWakeLock();
+ break;
+
+ case PAUSE_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofencePause(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ releaseWakeLock();
+ break;
+
+ case RESUME_GEOFENCE_CALLBACK:
+ geofenceId = msg.arg1;
+ callback = mGeofences.get(geofenceId);
+ if (callback == null) return;
+
+ try {
+ callback.onGeofenceResume(geofenceId, msg.arg2);
+ } catch (RemoteException e) {}
+ releaseWakeLock();
+ break;
+
+ case GEOFENCE_TRANSITION_CALLBACK:
+ GeofenceTransition geofenceTransition = (GeofenceTransition)(msg.obj);
+ callback = mGeofences.get(geofenceTransition.mGeofenceId);
+
+ if (DEBUG) Log.d(TAG, "GeofenceTransistionCallback: GPS : GeofenceId: " +
+ geofenceTransition.mGeofenceId +
+ "Transition: " + geofenceTransition.mTransition +
+ "Location: " + geofenceTransition.mLocation + ":" + mGeofences);
+
+ try {
+ callback.onGeofenceChange(
+ geofenceTransition.mGeofenceId, geofenceTransition.mTransition,
+ geofenceTransition.mLocation, geofenceTransition.mTimestamp,
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE);
+ } catch (RemoteException e) {}
+ releaseWakeLock();
+ break;
+ }
+ }
+ };
+
+ // All operations on mCallbacks
+ private Handler mCallbacksHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ int monitoringType;
+ ArrayList<IGeofenceHardwareCallback> callbackList;
+ IGeofenceHardwareCallback callback;
+
+ switch (msg.what) {
+ case GPS_GEOFENCE_STATUS:
+ Location location = (Location) msg.obj;
+ int val = msg.arg1;
+ boolean available;
+ available = (val == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE ?
+ true : false);
+ callbackList = mCallbacks[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE];
+ if (callbackList == null) return;
+
+ if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available);
+
+ for (IGeofenceHardwareCallback c: callbackList) {
+ try {
+ c.onMonitoringSystemChange(
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, available,
+ location);
+ } catch (RemoteException e) {}
+ }
+ releaseWakeLock();
+ break;
+ case CALLBACK_ADD:
+ monitoringType = msg.arg1;
+ callback = (IGeofenceHardwareCallback) msg.obj;
+ callbackList = mCallbacks[monitoringType];
+ if (callbackList == null) {
+ callbackList = new ArrayList<IGeofenceHardwareCallback>();
+ mCallbacks[monitoringType] = callbackList;
+ }
+ if (!callbackList.contains(callback)) callbackList.add(callback);
+ break;
+ case CALLBACK_REMOVE:
+ monitoringType = msg.arg1;
+ callback = (IGeofenceHardwareCallback) msg.obj;
+ callbackList = mCallbacks[monitoringType];
+ if (callbackList != null) {
+ callbackList.remove(callback);
+ }
+ break;
+ }
+ }
+ };
+
+ private class GeofenceTransition {
+ private int mGeofenceId, mTransition;
+ private long mTimestamp;
+ private Location mLocation;
+
+ GeofenceTransition(int geofenceId, int transition, long timestamp, Location location) {
+ mGeofenceId = geofenceId;
+ mTransition = transition;
+ mTimestamp = timestamp;
+ mLocation = location;
+ }
+ }
+
+ private void setMonitorAvailability(int monitor, int val) {
+ synchronized (mSupportedMonitorTypes) {
+ mSupportedMonitorTypes[monitor] = val;
+ }
+ }
+
+
+ int getMonitoringResolutionLevel(int monitoringType) {
+ switch (monitoringType) {
+ case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
+ return RESOLUTION_LEVEL_FINE;
+ }
+ return RESOLUTION_LEVEL_NONE;
+ }
+
+ int getAllowedResolutionLevel(int pid, int uid) {
+ if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return RESOLUTION_LEVEL_FINE;
+ } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ pid, uid) == PackageManager.PERMISSION_GRANTED) {
+ return RESOLUTION_LEVEL_COARSE;
+ } else {
+ return RESOLUTION_LEVEL_NONE;
+ }
+ }
+
+ private int getGeofenceStatus(int status) {
+ switch (status) {
+ case GPS_GEOFENCE_OPERATION_SUCCESS:
+ return GeofenceHardware.GEOFENCE_SUCCESS;
+ case GPS_GEOFENCE_ERROR_GENERIC:
+ return GeofenceHardware.GEOFENCE_FAILURE;
+ case GPS_GEOFENCE_ERROR_ID_EXISTS:
+ return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
+ case GPS_GEOFENCE_ERROR_INVALID_TRANSITION:
+ return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
+ case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES:
+ return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
+ case GPS_GEOFENCE_ERROR_ID_UNKNOWN:
+ return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
+ }
+ return -1;
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareService.java b/core/java/android/hardware/location/GeofenceHardwareService.java
new file mode 100644
index 0000000..0eccee6
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareService.java
@@ -0,0 +1,134 @@
+/*
+ * 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.Manifest;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.location.IGpsGeofenceHardware;
+import android.os.Binder;
+import android.os.IBinder;
+
+/**
+ * Service that handles hardware geofencing.
+ *
+ * @hide
+ */
+public class GeofenceHardwareService extends Service {
+ private GeofenceHardwareImpl mGeofenceHardwareImpl;
+ private Context mContext;
+
+ @Override
+ public void onCreate() {
+ mContext = this;
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ @Override
+ public boolean onUnbind(Intent intent) {
+ return false;
+ }
+
+ @Override
+ public void onDestroy() {
+ mGeofenceHardwareImpl = null;
+ }
+
+
+ private void checkPermission(int pid, int uid, int monitoringType) {
+ if (mGeofenceHardwareImpl.getAllowedResolutionLevel(pid, uid) <
+ mGeofenceHardwareImpl.getMonitoringResolutionLevel(monitoringType)) {
+ throw new SecurityException("Insufficient permissions to access hardware geofence for"
+ + " type: " + monitoringType);
+ }
+ }
+
+ private IBinder mBinder = new IGeofenceHardware.Stub() {
+ public void setGpsGeofenceHardware(IGpsGeofenceHardware service) {
+ mGeofenceHardwareImpl.setGpsHardwareGeofence(service);
+ }
+
+ public int[] getMonitoringTypesAndStatus() {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ return mGeofenceHardwareImpl.getMonitoringTypesAndStatus();
+ }
+
+ public boolean addCircularFence(int id, double lat, double longitude, double radius,
+ int lastTransition, int monitorTransitions, int
+ notificationResponsiveness, int unknownTimer, int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.addCircularFence(id, lat, longitude, radius,
+ lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer,
+ monitoringType, callback);
+ }
+
+ public boolean removeGeofence(int id, int monitoringType) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.removeGeofence(id, monitoringType);
+ }
+
+ public boolean pauseGeofence(int id, int monitoringType) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.pauseGeofence(id, monitoringType);
+ }
+
+ public boolean resumeGeofence(int id, int monitorTransitions, int monitoringType) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.resumeGeofence(id, monitorTransitions, monitoringType);
+ }
+
+ public boolean registerForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.registerForMonitorStateChangeCallback(monitoringType,
+ callback);
+ }
+
+ public boolean unregisterForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback) {
+ mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware geofence");
+
+ checkPermission(Binder.getCallingPid(), Binder.getCallingUid(), monitoringType);
+ return mGeofenceHardwareImpl.unregisterForMonitorStateChangeCallback(monitoringType,
+ callback);
+ }
+ };
+}
diff --git a/core/java/android/hardware/location/IGeofenceHardware.aidl b/core/java/android/hardware/location/IGeofenceHardware.aidl
new file mode 100644
index 0000000..4ba02b8
--- /dev/null
+++ b/core/java/android/hardware/location/IGeofenceHardware.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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/LICENS E-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.location.IGpsGeofenceHardware;
+import android.hardware.location.IGeofenceHardwareCallback;
+
+/** @hide */
+interface IGeofenceHardware {
+ void setGpsGeofenceHardware(in IGpsGeofenceHardware service);
+ int[] getMonitoringTypesAndStatus();
+ boolean addCircularFence(int id, double lat, double longitude, double radius,
+ int lastTransition, int monitorTransitions, int notificationResponsiveness,
+ int unknownTimer, int monitoringType, in IGeofenceHardwareCallback callback);
+ boolean removeGeofence(int id, int monitoringType);
+ boolean pauseGeofence(int id, int monitoringType);
+ boolean resumeGeofence(int id, int monitorTransitions, int monitoringType);
+ boolean registerForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback);
+ boolean unregisterForMonitorStateChangeCallback(int monitoringType,
+ IGeofenceHardwareCallback callback);
+}
diff --git a/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl b/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl
new file mode 100644
index 0000000..678fc49
--- /dev/null
+++ b/core/java/android/hardware/location/IGeofenceHardwareCallback.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.location.Location;
+
+/** @hide */
+oneway interface IGeofenceHardwareCallback {
+ void onMonitoringSystemChange(int monitoringType, boolean available, in Location location);
+ void onGeofenceChange(int geofenceId, int transition, in Location location,
+ long timestamp, int monitoringType);
+ void onGeofenceAdd(int geofenceId, int status);
+ void onGeofenceRemove(int geofenceId, int status);
+ void onGeofencePause(int geofenceId, int status);
+ void onGeofenceResume(int geofenceId, int status);
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0417710..1e4deb1b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -616,6 +616,14 @@
android:label="@string/permlab_installLocationProvider"
android:description="@string/permdesc_installLocationProvider" />
+ <!-- Allows an application to use location features in hardware,
+ such as the geofencing api
+ Protected by signature|system protection level -->
+ <permission android:name="android.permission.LOCATION_HARDWARE"
+ android:permissionGroup="android.permission-group.LOCATION"
+ android:protectionLevel="signature|system" />
+ <uses-permission android:name="android.permission.LOCATION_HARDWARE"/>
+
<!-- ======================================= -->
<!-- Permissions for accessing networks -->
<!-- ======================================= -->
@@ -2357,6 +2365,9 @@
android:permission="android.permission.MASTER_CLEAR"
android:exported="true" />
+ <service android:name="android.hardware.location.GeofenceHardwareService"
+ android:permission="android.permission.LOCATION_HARDWARE"
+ android:exported="false" />
</application>
</manifest>
diff --git a/location/java/android/location/IGeofenceProvider.aidl b/location/java/android/location/IGeofenceProvider.aidl
new file mode 100644
index 0000000..5a5fdc6
--- /dev/null
+++ b/location/java/android/location/IGeofenceProvider.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.location;
+
+import android.hardware.location.IGeofenceHardware;
+
+/**
+ * An interface for location providers implementing the Geofencing service
+ *
+ * {@hide}
+ */
+interface IGeofenceProvider {
+ void setGeofenceHardware(in IGeofenceHardware proxy);
+}
diff --git a/location/java/android/location/IGpsGeofenceHardware.aidl b/location/java/android/location/IGpsGeofenceHardware.aidl
new file mode 100644
index 0000000..764bf8e
--- /dev/null
+++ b/location/java/android/location/IGpsGeofenceHardware.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.location;
+
+/**
+ * GPS hardware geofence
+ *
+ * @hide
+ */
+interface IGpsGeofenceHardware
+{
+ boolean isHardwareGeofenceSupported();
+ boolean addCircularHardwareGeofence(int geofenceId, double latitude, double
+ longitude, double radius, int lastTransition, int monitorTransition,
+ int notificationResponsiveness, int unknownTimer);
+ boolean removeHardwareGeofence(int geofenceId);
+ boolean pauseHardwareGeofence(int geofenceId);
+ boolean resumeHardwareGeofence(int geofenceId, int monitorTransition);
+}
diff --git a/location/lib/java/com/android/location/provider/GeofenceProvider.java b/location/lib/java/com/android/location/provider/GeofenceProvider.java
new file mode 100644
index 0000000..2618f34
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/GeofenceProvider.java
@@ -0,0 +1,67 @@
+/*
+ * 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 com.android.location.provider;
+
+import android.hardware.location.GeofenceHardware;
+import android.hardware.location.IGeofenceHardware;
+import android.os.IBinder;
+
+import android.location.IGeofenceProvider;
+import android.util.Log;
+
+import java.lang.Long;
+
+/**
+ * Base class for geofence providers implemented as unbundled services.
+ *
+ * <p>Geofence providers can be implemented as services and return the result of
+ * {@link com.android.location.provider.GeofenceProvider#getBinder()} in its getBinder() method.
+ *
+ * <p>IMPORTANT: This class is effectively a public API for unbundled
+ * applications, and must remain API stable. See README.txt in the root
+ * of this package for more information.
+ */
+public abstract class GeofenceProvider {
+
+ private GeofenceHardware mGeofenceHardware;
+
+ private IGeofenceProvider.Stub mProvider = new IGeofenceProvider.Stub() {
+ public void setGeofenceHardware(IGeofenceHardware hardwareProxy) {
+ mGeofenceHardware = new GeofenceHardware(hardwareProxy);
+ onGeofenceHardwareChange(mGeofenceHardware);
+ }
+ };
+
+ /**
+ * Returns the Binder interface for the geofence provider.
+ * This is intended to be used for the onBind() method of
+ * a service that implements a geofence service.
+ *
+ * @return the IBinder instance for the provider
+ */
+ public IBinder getBinder() {
+ return mProvider;
+ }
+
+ /**
+ * Called when GeofenceHardware object becomes available.
+ *
+ * @param geofenceHardware Geofence Hardware object. This can be null
+ * when for some reason the service connection gets disconnected.
+ */
+ public abstract void onGeofenceHardwareChange(GeofenceHardware geofenceHardware);
+}
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index bcb7cb7..1e1cf5a 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -54,19 +54,17 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
-
-import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GeofenceProxy;
import com.android.server.location.GeofenceManager;
import com.android.server.location.GpsLocationProvider;
import com.android.server.location.LocationBlacklist;
@@ -338,11 +336,11 @@
addProviderLocked(passiveProvider);
mEnabledProviders.add(passiveProvider.getName());
mPassiveProvider = passiveProvider;
+ // Create a gps location provider
+ GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this,
+ mLocationHandler.getLooper());
if (GpsLocationProvider.isSupported()) {
- // Create a gps location provider
- GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this,
- mLocationHandler.getLooper());
mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
addProviderLocked(gpsProvider);
@@ -406,6 +404,14 @@
if (mGeocodeProvider == null) {
Slog.e(TAG, "no geocoder provider found");
}
+
+ // bind to geofence provider
+ GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, providerPackageNames,
+ mLocationHandler, gpsProvider.getGpsGeofenceProxy());
+ if (provider == null) {
+ Slog.e(TAG, "no geofence provider found");
+ }
+
}
/**
diff --git a/services/java/com/android/server/location/GeofenceProxy.java b/services/java/com/android/server/location/GeofenceProxy.java
new file mode 100644
index 0000000..36e9fcc
--- /dev/null
+++ b/services/java/com/android/server/location/GeofenceProxy.java
@@ -0,0 +1,154 @@
+/*
+ * 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 com.android.server.location;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.hardware.location.GeofenceHardwareService;
+import android.hardware.location.IGeofenceHardware;
+import android.location.IGeofenceProvider;
+import android.location.IGpsGeofenceHardware;
+import android.content.Context;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import com.android.server.ServiceWatcher;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+public final class GeofenceProxy {
+ private static final String TAG = "GeofenceProxy";
+ private static final String SERVICE_ACTION =
+ "com.android.location.service.GeofenceProvider";
+ private ServiceWatcher mServiceWatcher;
+ private Context mContext;
+ private IGeofenceHardware mGeofenceHardware;
+ private IGpsGeofenceHardware mGpsGeofenceHardware;
+
+ private static final int GEOFENCE_PROVIDER_CONNECTED = 1;
+ private static final int GEOFENCE_HARDWARE_CONNECTED = 2;
+ private static final int GEOFENCE_HARDWARE_DISCONNECTED = 3;
+ private static final int GEOFENCE_GPS_HARDWARE_CONNECTED = 4;
+ private static final int GEOFENCE_GPS_HARDWARE_DISCONNECTED = 5;
+
+ private Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mHandler.sendEmptyMessage(GEOFENCE_PROVIDER_CONNECTED);
+ }
+ };
+
+ public static GeofenceProxy createAndBind(Context context,
+ List<String> initialPackageNames, Handler handler, IGpsGeofenceHardware gpsGeofence) {
+ GeofenceProxy proxy = new GeofenceProxy(context, initialPackageNames, handler, gpsGeofence);
+ if (proxy.bindGeofenceProvider()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ private GeofenceProxy(Context context, List<String> initialPackageName, Handler handler,
+ IGpsGeofenceHardware gpsGeofence) {
+ mContext = context;
+ mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, initialPackageName,
+ mRunnable, handler);
+ mGpsGeofenceHardware = gpsGeofence;
+ bindHardwareGeofence();
+ }
+
+ private boolean bindGeofenceProvider() {
+ return mServiceWatcher.start();
+ }
+
+ private IGeofenceProvider getGeofenceProviderService() {
+ return IGeofenceProvider.Stub.asInterface(mServiceWatcher.getBinder());
+ }
+
+ private void bindHardwareGeofence() {
+ mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class),
+ mServiceConnection, Context.BIND_AUTO_CREATE, UserHandle.OWNER);
+ }
+
+ private ServiceConnection mServiceConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mGeofenceHardware = IGeofenceHardware.Stub.asInterface(service);
+ mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_CONNECTED);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mGeofenceHardware = null;
+ mHandler.sendEmptyMessage(GEOFENCE_HARDWARE_DISCONNECTED);
+ }
+ };
+
+ private void setGeofenceHardwareInProvider() {
+ try {
+ getGeofenceProviderService().setGeofenceHardware(mGeofenceHardware);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote Exception: setGeofenceHardwareInProvider: " + e);
+ }
+ }
+
+ private void setGpsGeofence() {
+ try {
+ mGeofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error while connecting to GeofenceHardwareService");
+ }
+ }
+
+
+ // This needs to be reworked, when more services get added,
+ // Might need a state machine or add a framework utility class,
+ private Handler mHandler = new Handler() {
+ private boolean mGeofenceHardwareConnected = false;
+ private boolean mGeofenceProviderConnected = false;
+
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case GEOFENCE_PROVIDER_CONNECTED:
+ mGeofenceProviderConnected = true;
+ if (mGeofenceHardwareConnected) {
+ setGeofenceHardwareInProvider();
+ }
+ break;
+ case GEOFENCE_HARDWARE_CONNECTED:
+ setGpsGeofence();
+ mGeofenceHardwareConnected = true;
+ if (mGeofenceProviderConnected) {
+ setGeofenceHardwareInProvider();
+ }
+ break;
+ case GEOFENCE_HARDWARE_DISCONNECTED:
+ mGeofenceHardwareConnected = false;
+ setGeofenceHardwareInProvider();
+ break;
+ }
+ }
+ };
+}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 3552b6a..1ebff67 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -24,7 +24,10 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
+import android.hardware.location.GeofenceHardwareImpl;
+import android.hardware.location.IGeofenceHardware;
import android.location.Criteria;
+import android.location.IGpsGeofenceHardware;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
import android.location.ILocationManager;
@@ -314,6 +317,8 @@
// only modified on handler thread
private WorkSource mClientSource = new WorkSource();
+ private GeofenceHardwareImpl mGeofenceHardwareImpl;
+
private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
@Override
public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
@@ -367,6 +372,10 @@
return mGpsStatusProvider;
}
+ public IGpsGeofenceHardware getGpsGeofenceProxy() {
+ return mGpsGeofenceBinder;
+ }
+
private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -918,6 +927,31 @@
return result;
}
+ private IGpsGeofenceHardware mGpsGeofenceBinder = new IGpsGeofenceHardware.Stub() {
+ public boolean isHardwareGeofenceSupported() {
+ return native_is_geofence_supported();
+ }
+
+ public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
+ double longitude, double radius, int lastTransition, int monitorTransitions,
+ int notificationResponsiveness, int unknownTimer) {
+ return native_add_geofence(geofenceId, latitude, longitude, radius,
+ lastTransition, monitorTransitions, notificationResponsiveness, unknownTimer);
+ }
+
+ public boolean removeHardwareGeofence(int geofenceId) {
+ return native_remove_geofence(geofenceId);
+ }
+
+ public boolean pauseHardwareGeofence(int geofenceId) {
+ return native_pause_geofence(geofenceId);
+ }
+
+ public boolean resumeHardwareGeofence(int geofenceId, int monitorTransition) {
+ return native_resume_geofence(geofenceId, monitorTransition);
+ }
+ };
+
private boolean deleteAidingData(Bundle extras) {
int flags;
@@ -1017,6 +1051,7 @@
return ((mEngineCapabilities & capability) != 0);
}
+
/**
* called from native code to update our position.
*/
@@ -1320,6 +1355,73 @@
sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
}
+ /**
+ * Called from native to report GPS Geofence transition
+ * All geofence callbacks are called on the same thread
+ */
+ private void reportGeofenceTransition(int geofenceId, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp, int transition, long transitionTimestamp) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceTransition(geofenceId, flags, latitude, longitude,
+ altitude, speed, bearing, accuracy, timestamp, transition, transitionTimestamp);
+ }
+
+ /**
+ * called from native code to report GPS status change.
+ */
+ private void reportGeofenceStatus(int status, int flags, double latitude,
+ double longitude, double altitude, float speed, float bearing, float accuracy,
+ long timestamp) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceStatus(status, flags, latitude, longitude, altitude,
+ speed, bearing, accuracy, timestamp);
+ }
+
+ /**
+ * called from native code - Geofence Add callback
+ */
+ private void reportGeofenceAddStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceAddStatus(geofenceId, status);
+ }
+
+ /**
+ * called from native code - Geofence Remove callback
+ */
+ private void reportGeofenceRemoveStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceRemoveStatus(geofenceId, status);
+ }
+
+ /**
+ * called from native code - Geofence Pause callback
+ */
+ private void reportGeofencePauseStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofencePauseStatus(geofenceId, status);
+ }
+
+ /**
+ * called from native code - Geofence Resume callback
+ */
+ private void reportGeofenceResumeStatus(int geofenceId, int status) {
+ if (mGeofenceHardwareImpl == null) {
+ mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+ }
+ mGeofenceHardwareImpl.reportGpsGeofenceResumeStatus(geofenceId, status);
+ }
+
//=============================================================
// NI Client support
//=============================================================
@@ -1650,4 +1752,13 @@
private native void native_update_network_state(boolean connected, int type,
boolean roaming, boolean available, String extraInfo, String defaultAPN);
+
+ // Hardware Geofence support.
+ private static native boolean native_is_geofence_supported();
+ private static native boolean native_add_geofence(int geofenceId, double latitude,
+ double longitude, double radius, int lastTransition,int monitorTransitions,
+ int notificationResponsivenes, int unknownTimer);
+ private static native boolean native_remove_geofence(int geofenceId);
+ private static native boolean native_resume_geofence(int geofenceId, int transitions);
+ private static native boolean native_pause_geofence(int geofenceId);
}
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index 036fc43..98de12a 100644
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -43,6 +43,12 @@
static jmethodID method_requestRefLocation;
static jmethodID method_requestSetID;
static jmethodID method_requestUtcTime;
+static jmethodID method_reportGeofenceTransition;
+static jmethodID method_reportGeofenceStatus;
+static jmethodID method_reportGeofenceAddStatus;
+static jmethodID method_reportGeofenceRemoveStatus;
+static jmethodID method_reportGeofencePauseStatus;
+static jmethodID method_reportGeofenceResumeStatus;
static const GpsInterface* sGpsInterface = NULL;
static const GpsXtraInterface* sGpsXtraInterface = NULL;
@@ -50,6 +56,7 @@
static const GpsNiInterface* sGpsNiInterface = NULL;
static const GpsDebugInterface* sGpsDebugInterface = NULL;
static const AGpsRilInterface* sAGpsRilInterface = NULL;
+static const GpsGeofencingInterface* sGpsGeofencingInterface = NULL;
// temporary storage for GPS callbacks
static GpsSvStatus sGpsSvStatus;
@@ -107,7 +114,7 @@
static void set_capabilities_callback(uint32_t capabilities)
{
- ALOGD("set_capabilities_callback: %ld\n", capabilities);
+ ALOGD("set_capabilities_callback: %du\n", capabilities);
JNIEnv* env = AndroidRuntime::getJNIEnv();
env->CallVoidMethod(mCallbacksObj, method_setEngineCapabilities, capabilities);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -233,6 +240,97 @@
create_thread_callback,
};
+static void gps_geofence_transition_callback(int32_t geofence_id, GpsLocation* location,
+ int32_t transition, GpsUtcTime timestamp)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceTransition, geofence_id,
+ location->flags, (jdouble)location->latitude, (jdouble)location->longitude,
+ (jdouble)location->altitude,
+ (jfloat)location->speed, (jfloat)location->bearing,
+ (jfloat)location->accuracy, (jlong)location->timestamp,
+ transition, timestamp);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_status_callback(int32_t status, GpsLocation* location)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ jint flags = 0;
+ jdouble latitude = 0;
+ jdouble longitude = 0;
+ jdouble altitude = 0;
+ jfloat speed = 0;
+ jfloat bearing = 0;
+ jfloat accuracy = 0;
+ jlong timestamp = 0;
+ if (location != NULL) {
+ flags = location->flags;
+ latitude = location->latitude;
+ longitude = location->longitude;
+ altitude = location->altitude;
+ speed = location->speed;
+ bearing = location->bearing;
+ accuracy = location->accuracy;
+ timestamp = location->timestamp;
+ }
+
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, status,
+ flags, latitude, longitude, altitude, speed, bearing, accuracy, timestamp);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_add_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_add_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceAddStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_remove_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_remove_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceRemoveStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_resume_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_resume_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofenceResumeStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+static void gps_geofence_pause_callback(int32_t geofence_id, int32_t status)
+{
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (status != GPS_GEOFENCE_OPERATION_SUCCESS) {
+ ALOGE("Error in geofence_pause_callback: %d\n", status);
+ }
+ env->CallVoidMethod(mCallbacksObj, method_reportGeofencePauseStatus, geofence_id, status);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+};
+
+GpsGeofenceCallbacks sGpsGeofenceCallbacks = {
+ gps_geofence_transition_callback,
+ gps_geofence_status_callback,
+ gps_geofence_add_callback,
+ gps_geofence_remove_callback,
+ gps_geofence_pause_callback,
+ gps_geofence_resume_callback,
+ create_thread_callback,
+};
+
static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
int err;
hw_module_t* module;
@@ -249,6 +347,18 @@
method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V");
method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V");
method_requestUtcTime = env->GetMethodID(clazz,"requestUtcTime","()V");
+ method_reportGeofenceTransition = env->GetMethodID(clazz,"reportGeofenceTransition",
+ "(IIDDDFFFJIJ)V");
+ method_reportGeofenceStatus = env->GetMethodID(clazz,"reportGeofenceStatus",
+ "(IIDDDFFFJ)V");
+ method_reportGeofenceAddStatus = env->GetMethodID(clazz,"reportGeofenceAddStatus",
+ "(II)V");
+ method_reportGeofenceRemoveStatus = env->GetMethodID(clazz,"reportGeofenceRemoveStatus",
+ "(II)V");
+ method_reportGeofenceResumeStatus = env->GetMethodID(clazz,"reportGeofenceResumeStatus",
+ "(II)V");
+ method_reportGeofencePauseStatus = env->GetMethodID(clazz,"reportGeofencePauseStatus",
+ "(II)V");
err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
if (err == 0) {
@@ -270,6 +380,8 @@
(const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE);
sAGpsRilInterface =
(const AGpsRilInterface*)sGpsInterface->get_extension(AGPS_RIL_INTERFACE);
+ sGpsGeofencingInterface =
+ (const GpsGeofencingInterface*)sGpsInterface->get_extension(GPS_GEOFENCING_INTERFACE);
}
}
@@ -287,7 +399,7 @@
if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
return false;
- // if XTRA initialization fails we will disable it by sGpsXtraInterface to null,
+ // if XTRA initialization fails we will disable it by sGpsXtraInterface to NULL,
// but continue to allow the rest of the GPS interface to work.
if (sGpsXtraInterface && sGpsXtraInterface->init(&sGpsXtraCallbacks) != 0)
sGpsXtraInterface = NULL;
@@ -297,6 +409,8 @@
sGpsNiInterface->init(&sGpsNiCallbacks);
if (sAGpsRilInterface)
sAGpsRilInterface->init(&sAGpsRilCallbacks);
+ if (sGpsGeofencingInterface)
+ sGpsGeofencingInterface->init(&sGpsGeofenceCallbacks);
return true;
}
@@ -565,6 +679,62 @@
}
}
+static jboolean android_location_GpsLocationProvider_is_geofence_supported(JNIEnv* env,
+ jobject obj) {
+ if (sGpsGeofencingInterface != NULL) {
+ return JNI_TRUE;
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_add_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id, jdouble latitude, jdouble longitude, jdouble radius,
+ jint last_transition, jint monitor_transition, jint notification_responsiveness,
+ jint unknown_timer) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->add_geofence_area(geofence_id, latitude, longitude,
+ radius, last_transition, monitor_transition, notification_responsiveness,
+ unknown_timer);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_remove_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->remove_geofence_area(geofence_id);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_pause_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->pause_geofence(geofence_id);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
+static jboolean android_location_GpsLocationProvider_resume_geofence(JNIEnv* env, jobject obj,
+ jint geofence_id, jint monitor_transition) {
+ if (sGpsGeofencingInterface != NULL) {
+ sGpsGeofencingInterface->resume_geofence(geofence_id, monitor_transition);
+ return JNI_TRUE;
+ } else {
+ ALOGE("Geofence interface not available");
+ }
+ return JNI_FALSE;
+}
+
static JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
@@ -591,6 +761,11 @@
{"native_agps_ni_message", "([BI)V", (void *)android_location_GpsLocationProvider_agps_send_ni_message},
{"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state},
{"native_update_network_state", "(ZIZZLjava/lang/String;Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_update_network_state },
+ {"native_is_geofence_supported", "()Z", (void*) android_location_GpsLocationProvider_is_geofence_supported},
+ {"native_add_geofence", "(IDDDIIII)Z", (void *)android_location_GpsLocationProvider_add_geofence},
+ {"native_remove_geofence", "(I)Z", (void *)android_location_GpsLocationProvider_remove_geofence},
+ {"native_pause_geofence", "(I)Z", (void *)android_location_GpsLocationProvider_pause_geofence},
+ {"native_resume_geofence", "(II)Z", (void *)android_location_GpsLocationProvider_resume_geofence}
};
int register_android_server_location_GpsLocationProvider(JNIEnv* env)