GPS Hardware geofencing.

Add support for doing geofencing in hardware.

Change-Id: I6d5015190e8d84e1f4beb1010ed977a71c1622d0
This commit is contained in:
Jaikumar Ganesh
2013-04-03 12:22:18 -07:00
parent 8d06cc6d87
commit 8ce470dd4b
16 changed files with 1973 additions and 8 deletions

View File

@@ -124,6 +124,8 @@ LOCAL_SRC_FILES += \
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 \
@@ -207,10 +209,12 @@ LOCAL_SRC_FILES += \
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 \

View File

@@ -69,6 +69,7 @@ package android {
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";
@@ -10443,6 +10444,43 @@ package android.hardware.input {
}
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 {

View File

@@ -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);
}
}
}

View File

@@ -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) {
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
};
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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 -->
<!-- ======================================= -->
@@ -2350,6 +2358,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>

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -54,19 +54,17 @@ import android.os.Message;
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 @@ public class LocationManagerService extends ILocationManager.Stub {
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 @@ public class LocationManagerService extends ILocationManager.Stub {
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");
}
}
/**

View File

@@ -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;
}
}
};
}

View File

@@ -24,7 +24,10 @@ import android.content.Context;
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 @@ public class GpsLocationProvider implements LocationProviderInterface {
// 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 @@ public class GpsLocationProvider implements LocationProviderInterface {
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 @@ public class GpsLocationProvider implements LocationProviderInterface {
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 @@ public class GpsLocationProvider implements LocationProviderInterface {
return ((mEngineCapabilities & capability) != 0);
}
/**
* called from native code to update our position.
*/
@@ -1320,6 +1355,73 @@ public class GpsLocationProvider implements LocationProviderInterface {
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 @@ public class GpsLocationProvider implements LocationProviderInterface {
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);
}

View File

@@ -43,6 +43,12 @@ static jmethodID method_reportNiNotification;
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 AGpsInterface* sAGpsInterface = NULL;
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 nmea_callback(GpsUtcTime timestamp, const char* nmea, int length)
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 @@ AGpsRilCallbacks sAGpsRilCallbacks = {
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 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env,
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 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env,
(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 @@ static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject o
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 @@ static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject o
sGpsNiInterface->init(&sGpsNiCallbacks);
if (sAGpsRilInterface)
sAGpsRilInterface->init(&sAGpsRilCallbacks);
if (sGpsGeofencingInterface)
sGpsGeofencingInterface->init(&sGpsGeofenceCallbacks);
return true;
}
@@ -565,6 +679,62 @@ static void android_location_GpsLocationProvider_update_network_state(JNIEnv* en
}
}
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 @@ static JNINativeMethod sMethods[] = {
{"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)