Merge "Update location AppOp monitoring to respect settings. If a provider is disabled, we don't mark an app as actively using location just because it's requested that provider. Also updates the concept of high power to support third party custom providers (doesn't hard code gps but looks at the provider's actual stated power requirement)." into klp-dev
diff --git a/Android.mk b/Android.mk
index 22ee3a6..656e40c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -355,6 +355,7 @@
frameworks/base/core/java/android/content/SyncStats.aidl \
frameworks/base/core/java/android/content/res/Configuration.aidl \
frameworks/base/core/java/android/database/CursorWindow.aidl \
+ frameworks/base/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.aidl \
frameworks/base/core/java/android/net/Uri.aidl \
frameworks/base/core/java/android/nfc/NdefMessage.aidl \
frameworks/base/core/java/android/nfc/NdefRecord.aidl \
@@ -391,6 +392,8 @@
frameworks/base/telephony/java/android/telephony/ServiceState.aidl \
frameworks/base/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
frameworks/base/telephony/java/com/android/internal/telephony/ITelephony.aidl \
+ frameworks/base/wifi/java/android/net/wifi/BatchedScanSettings.aidl \
+ frameworks/base/wifi/java/android/net/wifi/BatchedScanResult.aidl \
gen := $(TARGET_OUT_COMMON_INTERMEDIATES)/framework.aidl
$(gen): PRIVATE_SRC_FILES := $(aidl_files)
diff --git a/core/java/android/hardware/location/GeofenceHardware.java b/core/java/android/hardware/location/GeofenceHardware.java
index e67d0d7..21de9f5 100644
--- a/core/java/android/hardware/location/GeofenceHardware.java
+++ b/core/java/android/hardware/location/GeofenceHardware.java
@@ -15,16 +15,11 @@
*/
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
@@ -52,7 +47,7 @@
private IGeofenceHardware mService;
// Hardware systems that do geofence monitoring.
- static final int NUM_MONITORS = 1;
+ static final int NUM_MONITORS = 2;
/**
* Constant for geofence monitoring done by the GPS hardware.
@@ -60,6 +55,13 @@
public static final int MONITORING_TYPE_GPS_HARDWARE = 0;
/**
+ * Constant for geofence monitoring done by the Fused hardware.
+ *
+ * @hide
+ */
+ public static final int MONITORING_TYPE_FUSED_HARDWARE = 1;
+
+ /**
* Constant to indiciate that the monitoring system is currently
* available for monitoring geofences.
*/
@@ -124,8 +126,12 @@
*/
public static final int GEOFENCE_FAILURE = 5;
- static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
- static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
+ /**
+ * The constant used to indicate that the operation failed due to insufficient memory.
+ *
+ * @hide
+ */
+ public static final int GEOFENCE_ERROR_INSUFFICIENT_MEMORY = 6;
private HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>
mCallbacks = new HashMap<GeofenceHardwareCallback, GeofenceHardwareCallbackWrapper>();
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
index 77e3143..eac6620 100644
--- a/core/java/android/hardware/location/GeofenceHardwareImpl.java
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -18,23 +18,21 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.location.FusedBatchOptions;
+import android.location.IFusedGeofenceHardware;
import android.location.IGpsGeofenceHardware;
import android.location.Location;
import android.location.LocationManager;
-import android.os.Binder;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
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.
@@ -54,6 +52,7 @@
new ArrayList[GeofenceHardware.NUM_MONITORS];
private final ArrayList<Reaper> mReapers = new ArrayList<Reaper>();
+ private IFusedGeofenceHardware mFusedService;
private IGpsGeofenceHardware mGpsService;
private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS];
@@ -67,7 +66,7 @@
private static final int GEOFENCE_CALLBACK_BINDER_DIED = 6;
// mCallbacksHandler message types
- private static final int GPS_GEOFENCE_STATUS = 1;
+ private static final int GEOFENCE_STATUS = 1;
private static final int CALLBACK_ADD = 2;
private static final int CALLBACK_REMOVE = 3;
private static final int MONITOR_CALLBACK_BINDER_DIED = 4;
@@ -91,16 +90,6 @@
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);
@@ -113,6 +102,9 @@
// Init everything to unsupported.
setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
GeofenceHardware.MONITOR_UNSUPPORTED);
+ setMonitorAvailability(
+ GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
+ GeofenceHardware.MONITOR_UNSUPPORTED);
}
@@ -147,6 +139,22 @@
}
}
+ private void updateFusedHardwareAvailability() {
+ boolean fusedSupported;
+ try {
+ fusedSupported = mFusedService.isSupported();
+ } catch(RemoteException e) {
+ Log.e(TAG, "RemoteException calling LocationManagerService");
+ fusedSupported = false;
+ }
+
+ if(fusedSupported) {
+ setMonitorAvailability(
+ GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
+ GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE);
+ }
+ }
+
public void setGpsHardwareGeofence(IGpsGeofenceHardware service) {
if (mGpsService == null) {
mGpsService = service;
@@ -159,12 +167,39 @@
}
}
+ public void setFusedGeofenceHardware(IFusedGeofenceHardware service) {
+ if(mFusedService == null) {
+ mFusedService = service;
+ updateFusedHardwareAvailability();
+ } else if(service == null) {
+ mFusedService = null;
+ Log.w(TAG, "Fused Geofence Hardware service seems to have crashed");
+ } else {
+ Log.e(TAG, "Error: FusedService being set again");
+ }
+ }
+
public int[] getMonitoringTypes() {
+ boolean gpsSupported;
+ boolean fusedSupported;
synchronized (mSupportedMonitorTypes) {
- if (mSupportedMonitorTypes[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE] !=
- GeofenceHardware.MONITOR_UNSUPPORTED) {
- return new int[] {GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE};
+ gpsSupported = mSupportedMonitorTypes[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE]
+ != GeofenceHardware.MONITOR_UNSUPPORTED;
+ fusedSupported = mSupportedMonitorTypes[GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE]
+ != GeofenceHardware.MONITOR_UNSUPPORTED;
+ }
+
+ if(gpsSupported) {
+ if(fusedSupported) {
+ return new int[] {
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+ GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE };
+ } else {
+ return new int[] { GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE };
}
+ } else if (fusedSupported) {
+ return new int[] { GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE };
+ } else {
return new int[0];
}
}
@@ -213,6 +248,30 @@
result = false;
}
break;
+ case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
+ if(mFusedService == null) {
+ return false;
+ }
+ GeofenceHardwareRequest request = GeofenceHardwareRequest.createCircularGeofence(
+ latitude,
+ longitude,
+ radius);
+ request.setUnknownTimer(unknownTimer);
+ request.setNotificationResponsiveness(notificationResponsivenes);
+ request.setMonitorTransitions(monitorTransitions);
+ request.setLastTransition(lastTransition);
+
+ GeofenceHardwareRequestParcelable parcelableRequest =
+ new GeofenceHardwareRequestParcelable(geofenceId, request);
+ try {
+ mFusedService.addGeofences(
+ new GeofenceHardwareRequestParcelable[] { parcelableRequest });
+ result = true;
+ } catch(RemoteException e) {
+ Log.e(TAG, "AddGeofence: RemoteException calling LocationManagerService");
+ result = false;
+ }
+ break;
default:
result = false;
}
@@ -251,6 +310,18 @@
result = false;
}
break;
+ case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
+ if(mFusedService == null) {
+ return false;
+ }
+ try {
+ mFusedService.removeGeofences(new int[] { geofenceId });
+ result = true;
+ } catch(RemoteException e) {
+ Log.e(TAG, "RemoveGeofence: RemoteException calling LocationManagerService");
+ result = false;
+ }
+ break;
default:
result = false;
}
@@ -278,6 +349,18 @@
result = false;
}
break;
+ case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
+ if(mFusedService == null) {
+ return false;
+ }
+ try {
+ mFusedService.pauseMonitoringGeofence(geofenceId);
+ result = true;
+ } catch(RemoteException e) {
+ Log.e(TAG, "PauseGeofence: RemoteException calling LocationManagerService");
+ result = false;
+ }
+ break;
default:
result = false;
}
@@ -306,6 +389,18 @@
result = false;
}
break;
+ case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
+ if(mFusedService == null) {
+ return false;
+ }
+ try {
+ mFusedService.resumeMonitoringGeofence(geofenceId, monitorTransition);
+ result = true;
+ } catch(RemoteException e) {
+ Log.e(TAG, "ResumeGeofence: RemoteException calling LocationManagerService");
+ result = false;
+ }
+ break;
default:
result = false;
}
@@ -334,127 +429,106 @@
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());
+ /**
+ * Used to report geofence transitions
+ */
+ public void reportGeofenceTransition(
+ int geofenceId,
+ Location location,
+ int transition,
+ long transitionTimestamp,
+ int monitoringType,
+ int sourcesUsed) {
+ if(location == null) {
+ Log.e(TAG, String.format("Invalid Geofence Transition: location=%p", location));
+ return;
}
- if ((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
- location.setAltitude(altitude);
- } else {
- location.removeAltitude();
+ if(DEBUG) {
+ Log.d(
+ TAG,
+ "GeofenceTransition| " + location + ", transition:" + transition +
+ ", transitionTimestamp:" + transitionTimestamp + ", monitoringType:" +
+ monitoringType + ", sourcesUsed:" + sourcesUsed);
}
- 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;
+
+ GeofenceTransition geofenceTransition = new GeofenceTransition(
+ geofenceId,
+ transition,
+ transitionTimestamp,
+ location,
+ monitoringType,
+ sourcesUsed);
+ acquireWakeLock();
+
+ Message message = mGeofenceHandler.obtainMessage(
+ GEOFENCE_TRANSITION_CALLBACK,
+ geofenceTransition);
+ message.sendToTarget();
}
/**
- * called from GpsLocationProvider to report geofence transition
+ * Used to report Monitor status changes.
*/
- 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);
+ public void reportGeofenceMonitorStatus(
+ int monitoringType,
+ int monitoringStatus,
+ Location location,
+ int source) {
+ // TODO: use the source if needed in the future
+ setMonitorAvailability(monitoringType, monitoringStatus);
acquireWakeLock();
- Message m = mGeofenceHandler.obtainMessage(GEOFENCE_TRANSITION_CALLBACK, t);
- mGeofenceHandler.sendMessage(m);
+ Message message = mCallbacksHandler.obtainMessage(GEOFENCE_STATUS, location);
+ message.arg1 = monitoringStatus;
+ message.arg2 = monitoringType;
+ message.sendToTarget();
}
/**
- * called from GpsLocationProvider to report GPS status change.
+ * Internal generic status report function for Geofence operations.
+ *
+ * @param operation The operation to be reported as defined internally.
+ * @param geofenceId The id of the geofence the operation is related to.
+ * @param operationStatus The status of the operation as defined in GeofenceHardware class. This
+ * status is independent of the statuses reported by different HALs.
*/
- 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_AVAILABLE :
- GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE);
- setMonitorAvailability(GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, val);
-
+ private void reportGeofenceOperationStatus(int operation, int geofenceId, int operationStatus) {
acquireWakeLock();
- Message m = mCallbacksHandler.obtainMessage(GPS_GEOFENCE_STATUS, location);
- m.arg1 = val;
- mCallbacksHandler.sendMessage(m);
+ Message message = mGeofenceHandler.obtainMessage(operation);
+ message.arg1 = geofenceId;
+ message.arg2 = operationStatus;
+ message.sendToTarget();
}
/**
- * called from GpsLocationProvider add geofence callback.
+ * Used to report the status of a Geofence Add operation.
*/
- 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);
+ public void reportGeofenceAddStatus(int geofenceId, int status) {
+ if(DEBUG) Log.d(TAG, "AddCallback| id:" + geofenceId + ", status:" + status);
+ reportGeofenceOperationStatus(ADD_GEOFENCE_CALLBACK, geofenceId, status);
}
/**
- * called from GpsLocationProvider remove geofence callback.
+ * Used to report the status of a Geofence Remove operation.
*/
- 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);
+ public void reportGeofenceRemoveStatus(int geofenceId, int status) {
+ if(DEBUG) Log.d(TAG, "RemoveCallback| id:" + geofenceId + ", status:" + status);
+ reportGeofenceOperationStatus(REMOVE_GEOFENCE_CALLBACK, geofenceId, status);
}
/**
- * called from GpsLocationProvider pause geofence callback.
+ * Used to report the status of a Geofence Pause operation.
*/
- 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);
+ public void reportGeofencePauseStatus(int geofenceId, int status) {
+ if(DEBUG) Log.d(TAG, "PauseCallbac| id:" + geofenceId + ", status" + status);
+ reportGeofenceOperationStatus(PAUSE_GEOFENCE_CALLBACK, geofenceId, status);
}
/**
- * called from GpsLocationProvider resume geofence callback.
+ * Used to report the status of a Geofence Resume operation.
*/
- 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);
+ public void reportGeofenceResumeStatus(int geofenceId, int status) {
+ if(DEBUG) Log.d(TAG, "ResumeCallback| id:" + geofenceId + ", status:" + status);
+ reportGeofenceOperationStatus(RESUME_GEOFENCE_CALLBACK, geofenceId, status);
}
// All operations on mGeofences
@@ -539,7 +613,7 @@
callback.onGeofenceTransition(
geofenceTransition.mGeofenceId, geofenceTransition.mTransition,
geofenceTransition.mLocation, geofenceTransition.mTimestamp,
- GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE);
+ geofenceTransition.mMonitoringType);
} catch (RemoteException e) {}
}
releaseWakeLock();
@@ -571,21 +645,20 @@
IGeofenceHardwareMonitorCallback callback;
switch (msg.what) {
- case GPS_GEOFENCE_STATUS:
+ case GEOFENCE_STATUS:
Location location = (Location) msg.obj;
int val = msg.arg1;
+ monitoringType = msg.arg2;
boolean available;
available = (val == GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE ?
true : false);
- callbackList = mCallbacks[GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE];
+ callbackList = mCallbacks[monitoringType];
if (callbackList != null) {
if (DEBUG) Log.d(TAG, "MonitoringSystemChangeCallback: GPS : " + available);
for (IGeofenceHardwareMonitorCallback c: callbackList) {
try {
- c.onMonitoringSystemChange(
- GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE, available,
- location);
+ c.onMonitoringSystemChange(monitoringType, available, location);
} catch (RemoteException e) {}
}
}
@@ -666,12 +739,22 @@
private int mGeofenceId, mTransition;
private long mTimestamp;
private Location mLocation;
+ private int mMonitoringType;
+ private int mSourcesUsed;
- GeofenceTransition(int geofenceId, int transition, long timestamp, Location location) {
+ GeofenceTransition(
+ int geofenceId,
+ int transition,
+ long timestamp,
+ Location location,
+ int monitoringType,
+ int sourcesUsed) {
mGeofenceId = geofenceId;
mTransition = transition;
mTimestamp = timestamp;
mLocation = location;
+ mMonitoringType = monitoringType;
+ mSourcesUsed = sourcesUsed;
}
}
@@ -686,6 +769,8 @@
switch (monitoringType) {
case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
return RESOLUTION_LEVEL_FINE;
+ case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
+ return RESOLUTION_LEVEL_FINE;
}
return RESOLUTION_LEVEL_NONE;
}
@@ -752,22 +837,4 @@
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/GeofenceHardwareRequestParcelable.aidl b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.aidl
new file mode 100644
index 0000000..b599d44
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable GeofenceHardwareRequestParcelable;
diff --git a/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java
new file mode 100644
index 0000000..40e7fc4
--- /dev/null
+++ b/core/java/android/hardware/location/GeofenceHardwareRequestParcelable.java
@@ -0,0 +1,151 @@
+/*
+ * 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.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+/**
+ * Geofence Hardware Request used for internal location services communication.
+ *
+ * @hide
+ */
+public final class GeofenceHardwareRequestParcelable implements Parcelable {
+ private GeofenceHardwareRequest mRequest;
+ private int mId;
+
+ public GeofenceHardwareRequestParcelable(int id, GeofenceHardwareRequest request) {
+ mId = id;
+ mRequest = request;
+ }
+
+ /**
+ * Returns the id of this request.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Returns the latitude of this geofence.
+ */
+ public double getLatitude() {
+ return mRequest.getLatitude();
+ }
+
+ /**
+ * Returns the longitude of this geofence.
+ */
+ public double getLongitude() {
+ return mRequest.getLongitude();
+ }
+
+ /**
+ * Returns the radius of this geofence.
+ */
+ public double getRadius() {
+ return mRequest.getRadius();
+ }
+
+ /**
+ * Returns transitions monitored for this geofence.
+ */
+ public int getMonitorTransitions() {
+ return mRequest.getMonitorTransitions();
+ }
+
+ /**
+ * Returns the unknownTimer of this geofence.
+ */
+ public int getUnknownTimer() {
+ return mRequest.getUnknownTimer();
+ }
+
+ /**
+ * Returns the notification responsiveness of this geofence.
+ */
+ public int getNotificationResponsiveness() {
+ return mRequest.getNotificationResponsiveness();
+ }
+
+ /**
+ * Returns the last transition of this geofence.
+ */
+ public int getLastTransition() {
+ return mRequest.getLastTransition();
+ }
+
+ /**
+ * Returns the type of the geofence for the current request.
+ */
+ int getType() {
+ return mRequest.getType();
+ }
+
+ /**
+ * Method definitions to support Parcelable operations.
+ */
+ public static final Parcelable.Creator<GeofenceHardwareRequestParcelable> CREATOR =
+ new Parcelable.Creator<GeofenceHardwareRequestParcelable>() {
+ @Override
+ public GeofenceHardwareRequestParcelable createFromParcel(Parcel parcel) {
+ int geofenceType = parcel.readInt();
+ if(geofenceType != GeofenceHardwareRequest.GEOFENCE_TYPE_CIRCLE) {
+ Log.e(
+ "GeofenceHardwareRequest",
+ String.format("Invalid Geofence type: %d", geofenceType));
+ return null;
+ }
+
+ GeofenceHardwareRequest request = GeofenceHardwareRequest.createCircularGeofence(
+ parcel.readDouble(),
+ parcel.readDouble(),
+ parcel.readDouble());
+ request.setLastTransition(parcel.readInt());
+ request.setMonitorTransitions(parcel.readInt());
+ request.setUnknownTimer(parcel.readInt());
+ request.setNotificationResponsiveness(parcel.readInt());
+
+ int id = parcel.readInt();
+ return new GeofenceHardwareRequestParcelable(id, request);
+ }
+
+ @Override
+ public GeofenceHardwareRequestParcelable[] newArray(int size) {
+ return new GeofenceHardwareRequestParcelable[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(getType());
+ parcel.writeDouble(getLatitude());
+ parcel.writeDouble(getLongitude());
+ parcel.writeDouble(getRadius());
+ parcel.writeInt(getLastTransition());
+ parcel.writeInt(getMonitorTransitions());
+ parcel.writeInt(getUnknownTimer());
+ parcel.writeInt(getNotificationResponsiveness());
+ parcel.writeInt(getId());
+ }
+}
diff --git a/core/java/android/hardware/location/GeofenceHardwareService.java b/core/java/android/hardware/location/GeofenceHardwareService.java
index 3bc70ee..fb238bd 100644
--- a/core/java/android/hardware/location/GeofenceHardwareService.java
+++ b/core/java/android/hardware/location/GeofenceHardwareService.java
@@ -20,6 +20,7 @@
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.location.IFusedGeofenceHardware;
import android.location.IGpsGeofenceHardware;
import android.os.Binder;
import android.os.IBinder;
@@ -68,6 +69,10 @@
mGeofenceHardwareImpl.setGpsHardwareGeofence(service);
}
+ public void setFusedGeofenceHardware(IFusedGeofenceHardware service) {
+ mGeofenceHardwareImpl.setFusedGeofenceHardware(service);
+ }
+
public int[] getMonitoringTypes() {
mContext.enforceCallingPermission(Manifest.permission.LOCATION_HARDWARE,
"Location Hardware permission not granted to access hardware geofence");
diff --git a/core/java/android/hardware/location/IGeofenceHardware.aidl b/core/java/android/hardware/location/IGeofenceHardware.aidl
index 6900070..8900166 100644
--- a/core/java/android/hardware/location/IGeofenceHardware.aidl
+++ b/core/java/android/hardware/location/IGeofenceHardware.aidl
@@ -16,6 +16,7 @@
package android.hardware.location;
+import android.location.IFusedGeofenceHardware;
import android.location.IGpsGeofenceHardware;
import android.hardware.location.IGeofenceHardwareCallback;
import android.hardware.location.IGeofenceHardwareMonitorCallback;
@@ -23,6 +24,7 @@
/** @hide */
interface IGeofenceHardware {
void setGpsGeofenceHardware(in IGpsGeofenceHardware service);
+ void setFusedGeofenceHardware(in IFusedGeofenceHardware service);
int[] getMonitoringTypes();
int getStatusOfMonitoringType(int monitoringType);
boolean addCircularFence(int id, int monitoringType, double lat, double longitude,
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7a82892..c0db23c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2379,7 +2379,7 @@
* when hiding the status bar with {@link #SYSTEM_UI_FLAG_FULLSCREEN} and/or hiding the
* navigation bar with {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION} instead of having the system
* clear these flags upon interaction. The system may compensate by temporarily overlaying
- * transparent system bars while also delivering the event.
+ * semi-transparent system bars while also delivering the event.
*/
public static final int SYSTEM_UI_FLAG_ALLOW_TRANSIENT = 0x00000800;
@@ -16540,7 +16540,9 @@
* @param visibility Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE},
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, {@link #SYSTEM_UI_FLAG_FULLSCREEN},
* {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION},
- * and {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
+ * {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_ALLOW_TRANSIENT},
+ * {@link #SYSTEM_UI_FLAG_TRANSPARENT_STATUS},
+ * and {@link #SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION}.
*/
public void setSystemUiVisibility(int visibility) {
if (visibility != mSystemUiVisibility) {
@@ -16552,11 +16554,13 @@
}
/**
- * Returns the last {@link #setSystemUiVisibility(int) that this view has requested.
+ * Returns the last {@link #setSystemUiVisibility(int)} that this view has requested.
* @return Bitwise-or of flags {@link #SYSTEM_UI_FLAG_LOW_PROFILE},
* {@link #SYSTEM_UI_FLAG_HIDE_NAVIGATION}, {@link #SYSTEM_UI_FLAG_FULLSCREEN},
* {@link #SYSTEM_UI_FLAG_LAYOUT_STABLE}, {@link #SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION},
- * and {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.
+ * {@link #SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}, {@link #SYSTEM_UI_FLAG_ALLOW_TRANSIENT},
+ * {@link #SYSTEM_UI_FLAG_TRANSPARENT_STATUS},
+ * and {@link #SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION}.
*/
public int getSystemUiVisibility() {
return mSystemUiVisibility;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4a472fc..400e892 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -353,6 +353,9 @@
Default value is 2 minutes. -->
<integer translatable="false" name="config_wifi_driver_stop_delay">120000</integer>
+ <!-- Wifi driver supports batched scan -->
+ <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
+
<!-- Flag indicating whether the we should enable the automatic brightness in Settings.
Software implementation will be used if config_hardware_auto_brightness_available is not set -->
<bool name="config_automatic_brightness_available">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cd0c872..14ae1e6 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -281,7 +281,8 @@
<java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" />
<java-symbol type="bool" name="config_useFixedVolume" />
<java-symbol type="bool" name="config_forceDefaultOrientation" />
-
+ <java-symbol type="bool" name="config_wifi_batched_scan_supported" />
+
<java-symbol type="integer" name="config_cursorWindowSize" />
<java-symbol type="integer" name="config_extraFreeKbytesAdjust" />
<java-symbol type="integer" name="config_extraFreeKbytesAbsolute" />
diff --git a/location/java/android/location/IFusedGeofenceHardware.aidl b/location/java/android/location/IFusedGeofenceHardware.aidl
index 9dbf1f4..d8c3585 100644
--- a/location/java/android/location/IFusedGeofenceHardware.aidl
+++ b/location/java/android/location/IFusedGeofenceHardware.aidl
@@ -16,8 +16,8 @@
package android.location;
-import android.location.Geofence;
-
+import android.hardware.location.GeofenceHardwareRequestParcelable;
+
/**
* Fused Geofence Hardware interface.
*
@@ -39,11 +39,9 @@
/**
* Adds a given list of geofences to the system.
*
- * @param geofenceIdsArray The list of geofence Ids to add.
- * @param geofencesArray the list of geofences to add.
+ * @param geofenceRequestsArray The list of geofences to add.
*/
- // TODO: [GeofenceIntegration] GeofenceHardwareRequest is not a parcelable class exposed in aidl
- void addGeofences(in int[] geofenceIdsArray, in Geofence[] geofencesArray);
+ void addGeofences(in GeofenceHardwareRequestParcelable[] geofenceRequestsArray);
/**
* Removes a give list of geofences from the system.
@@ -79,7 +77,8 @@
* the geofence.
* @param monitorTransitions The set of transitions to monitor.
* @param notificationResponsiveness The notification responsivness needed.
- * @param unknownTimer The time span associated with the
+ * @param unknownTimer The time span associated with the.
+ * @param sourcesToUse The source technologies to use.
*
* Remarks: keep the options as separate fields to be able to leverage the class
* GeofenceHardwareRequest without any changes
@@ -89,5 +88,6 @@
in int lastTransition,
in int monitorTransitions,
in int notificationResponsiveness,
- in int unknownTimer);
+ in int unknownTimer,
+ in int sourcesToUse);
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index bb0d248..a0e6dd1 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -3916,13 +3916,13 @@
Random rand = new Random();
mParams = params;
- try {
- if (mCs.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
- log("isMobileOk: not mobile capable");
- result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
- return result;
- }
+ if (mCs.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
+ log("isMobileOk: not mobile capable");
+ result = ConnectivityManager.CMP_RESULT_CODE_NO_CONNECTION;
+ return result;
+ }
+ try {
// Enable fail fast as we'll do retries here and use a
// hipri connection so the default connection stays active.
log("isMobileOk: start hipri url=" + params.mUrl);
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index f77363c..9761441 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -420,19 +420,7 @@
Slog.e(TAG, "no geocoder provider found");
}
- // bind to geofence provider
- GeofenceProxy provider = GeofenceProxy.createAndBind(mContext,
- com.android.internal.R.bool.config_enableGeofenceOverlay,
- com.android.internal.R.string.config_geofenceProviderPackageName,
- com.android.internal.R.array.config_locationProviderPackageNames,
- mLocationHandler,
- gpsProvider.getGpsGeofenceProxy());
- if (provider == null) {
- Slog.e(TAG, "no geofence provider found");
- }
-
// bind to fused provider
- // TODO: [GeofenceIntegration] bind #getGeofenceHardware() with the GeofenceProxy
FlpHardwareProvider flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
FusedProxy fusedProxy = FusedProxy.createAndBind(
mContext,
@@ -441,10 +429,21 @@
com.android.internal.R.bool.config_enableFusedLocationOverlay,
com.android.internal.R.string.config_fusedLocationProviderPackageName,
com.android.internal.R.array.config_locationProviderPackageNames);
-
if(fusedProxy == null) {
Slog.e(TAG, "No FusedProvider found.");
}
+
+ // bind to geofence provider
+ GeofenceProxy provider = GeofenceProxy.createAndBind(mContext,
+ com.android.internal.R.bool.config_enableGeofenceOverlay,
+ com.android.internal.R.string.config_geofenceProviderPackageName,
+ com.android.internal.R.array.config_locationProviderPackageNames,
+ mLocationHandler,
+ gpsProvider.getGpsGeofenceProxy(),
+ flpHardwareProvider.getGeofenceHardware());
+ if (provider == null) {
+ Slog.e(TAG, "no geofence provider found");
+ }
}
/**
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index a6d7e3c..be6119d 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -8062,12 +8062,10 @@
if (activity == null) {
Slog.w(TAG, "getAssistContextExtras error: no resumed activity");
validActivity = false;
- }
- if (activity.app == null || activity.app.thread == null) {
+ } else if (activity.app == null || activity.app.thread == null) {
Slog.w(TAG, "getAssistContextExtras error: no process for " + activity);
validActivity = false;
- }
- if (activity.app.pid == Binder.getCallingPid()) {
+ } else if (activity.app.pid == Binder.getCallingPid()) {
Slog.w(TAG, "getAssistContextExtras error: request process same as " + activity);
validActivity = false;
}
diff --git a/services/java/com/android/server/location/FlpHardwareProvider.java b/services/java/com/android/server/location/FlpHardwareProvider.java
index 226c18c..ebeccfb 100644
--- a/services/java/com/android/server/location/FlpHardwareProvider.java
+++ b/services/java/com/android/server/location/FlpHardwareProvider.java
@@ -16,12 +16,13 @@
package com.android.server.location;
+import android.hardware.location.GeofenceHardware;
import android.hardware.location.GeofenceHardwareImpl;
+import android.hardware.location.GeofenceHardwareRequestParcelable;
import android.hardware.location.IFusedLocationHardware;
import android.hardware.location.IFusedLocationHardwareSink;
import android.location.IFusedGeofenceHardware;
import android.location.FusedBatchOptions;
-import android.location.Geofence;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -49,6 +50,15 @@
private final Context mContext;
private final Object mLocationSinkLock = new Object();
+ // FlpHal result codes, they must be equal to the ones in fused_location.h
+ private static final int FLP_RESULT_SUCCESS = 0;
+ private static final int FLP_RESULT_ERROR = -1;
+ private static final int FLP_RESULT_INSUFFICIENT_MEMORY = -2;
+ private static final int FLP_RESULT_TOO_MANY_GEOFENCES = -3;
+ private static final int FLP_RESULT_ID_EXISTS = -4;
+ private static final int FLP_RESULT_ID_UNKNOWN = -5;
+ private static final int FLP_RESULT_INVALID_GEOFENCE_TRANSITION = -6;
+
public static FlpHardwareProvider getInstance(Context context) {
if (sSingletonInstance == null) {
sSingletonInstance = new FlpHardwareProvider(context);
@@ -120,29 +130,46 @@
Location location,
int transition,
long timestamp,
- int sourcesUsed
- ) {
- // TODO: [GeofenceIntegration] change GeofenceHardwareImpl to accept a location object
+ int sourcesUsed) {
+ getGeofenceHardwareSink().reportGeofenceTransition(
+ geofenceId,
+ updateLocationInformation(location),
+ transition,
+ timestamp,
+ GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
+ sourcesUsed);
}
private void onGeofenceMonitorStatus(int status, int source, Location location) {
- // TODO: [GeofenceIntegration]
+ getGeofenceHardwareSink().reportGeofenceMonitorStatus(
+ GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE,
+ status,
+ updateLocationInformation(location),
+ source);
}
private void onGeofenceAdd(int geofenceId, int result) {
- // TODO: [GeofenceIntegration] map between GPS and FLP results to pass a consistent status
+ getGeofenceHardwareSink().reportGeofenceAddStatus(
+ geofenceId,
+ translateToGeofenceHardwareStatus(result));
}
private void onGeofenceRemove(int geofenceId, int result) {
- // TODO: [GeofenceIntegration] map between GPS and FLP results to pass a consistent status
+ getGeofenceHardwareSink().reportGeofenceRemoveStatus(
+ geofenceId,
+ translateToGeofenceHardwareStatus(result));
}
private void onGeofencePause(int geofenceId, int result) {
- // TODO; [GeofenceIntegration] map between GPS and FLP results
+ getGeofenceHardwareSink().reportGeofencePauseStatus(
+ geofenceId,
+ translateToGeofenceHardwareStatus(result));
}
private void onGeofenceResume(int geofenceId, int result) {
- // TODO: [GeofenceIntegration] map between GPS and FLP results
+ getGeofenceHardwareSink().reportGeofenceResumeStatus(
+ geofenceId,
+ translateToGeofenceHardwareStatus(result));
}
/**
@@ -175,7 +202,8 @@
// FlpGeofencingInterface members
private native boolean nativeIsGeofencingSupported();
- private native void nativeAddGeofences(int[] geofenceIdsArray, Geofence[] geofencesArray);
+ private native void nativeAddGeofences(
+ GeofenceHardwareRequestParcelable[] geofenceRequestsArray);
private native void nativePauseGeofence(int geofenceId);
private native void nativeResumeGeofence(int geofenceId, int monitorTransitions);
private native void nativeModifyGeofenceOption(
@@ -281,8 +309,8 @@
}
@Override
- public void addGeofences(int[] geofenceIdsArray, Geofence[] geofencesArray) {
- nativeAddGeofences(geofenceIdsArray, geofencesArray);
+ public void addGeofences(GeofenceHardwareRequestParcelable[] geofenceRequestsArray) {
+ nativeAddGeofences(geofenceRequestsArray);
}
@Override
@@ -305,17 +333,15 @@
int lastTransition,
int monitorTransitions,
int notificationResponsiveness,
- int unknownTimer
- ) {
- // TODO: [GeofenceIntegration] set sourcesToUse to the right value
- // TODO: expose sourcesToUse externally when needed
+ int unknownTimer,
+ int sourcesToUse) {
nativeModifyGeofenceOption(
geofenceId,
lastTransition,
monitorTransitions,
notificationResponsiveness,
unknownTimer,
- /* sourcesToUse */ 0xFFFF);
+ sourcesToUse);
}
};
@@ -347,10 +373,39 @@
private GeofenceHardwareImpl getGeofenceHardwareSink() {
if (mGeofenceHardwareSink == null) {
- // TODO: [GeofenceIntegration] we need to register ourselves with GeofenceHardwareImpl
mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext);
}
return mGeofenceHardwareSink;
}
-}
\ No newline at end of file
+
+ private static int translateToGeofenceHardwareStatus(int flpHalResult) {
+ switch(flpHalResult) {
+ case FLP_RESULT_SUCCESS:
+ return GeofenceHardware.GEOFENCE_SUCCESS;
+ case FLP_RESULT_ERROR:
+ return GeofenceHardware.GEOFENCE_FAILURE;
+ // TODO: uncomment this once the ERROR definition is marked public
+ //case FLP_RESULT_INSUFFICIENT_MEMORY:
+ // return GeofenceHardware.GEOFENCE_ERROR_INSUFFICIENT_MEMORY;
+ case FLP_RESULT_TOO_MANY_GEOFENCES:
+ return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
+ case FLP_RESULT_ID_EXISTS:
+ return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
+ case FLP_RESULT_ID_UNKNOWN:
+ return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
+ case FLP_RESULT_INVALID_GEOFENCE_TRANSITION:
+ return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
+ default:
+ Log.e(TAG, String.format("Invalid FlpHal result code: %d", flpHalResult));
+ return GeofenceHardware.GEOFENCE_FAILURE;
+ }
+ }
+
+ private Location updateLocationInformation(Location location) {
+ location.setProvider(LocationManager.FUSED_PROVIDER);
+ // set the elapsed time-stamp just as GPS provider does
+ location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+ return location;
+ }
+}
diff --git a/services/java/com/android/server/location/GeofenceProxy.java b/services/java/com/android/server/location/GeofenceProxy.java
index f6be27b..a86c923 100644
--- a/services/java/com/android/server/location/GeofenceProxy.java
+++ b/services/java/com/android/server/location/GeofenceProxy.java
@@ -22,6 +22,7 @@
import android.hardware.location.IGeofenceHardware;
import android.location.IGeofenceProvider;
import android.location.IGpsGeofenceHardware;
+import android.location.IFusedGeofenceHardware;
import android.content.Context;
import android.os.Handler;
import android.os.IBinder;
@@ -44,6 +45,7 @@
private Context mContext;
private IGeofenceHardware mGeofenceHardware;
private IGpsGeofenceHardware mGpsGeofenceHardware;
+ private IFusedGeofenceHardware mFusedGeofenceHardware;
private static final int GEOFENCE_PROVIDER_CONNECTED = 1;
private static final int GEOFENCE_HARDWARE_CONNECTED = 2;
@@ -60,9 +62,11 @@
public static GeofenceProxy createAndBind(Context context,
int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence) {
+ int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence,
+ IFusedGeofenceHardware fusedGeofenceHardware) {
GeofenceProxy proxy = new GeofenceProxy(context, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId, handler, gpsGeofence);
+ defaultServicePackageNameResId, initialPackageNamesResId, handler, gpsGeofence,
+ fusedGeofenceHardware);
if (proxy.bindGeofenceProvider()) {
return proxy;
} else {
@@ -72,11 +76,13 @@
private GeofenceProxy(Context context,
int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence) {
+ int initialPackageNamesResId, Handler handler, IGpsGeofenceHardware gpsGeofence,
+ IFusedGeofenceHardware fusedGeofenceHardware) {
mContext = context;
mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
defaultServicePackageNameResId, initialPackageNamesResId, mRunnable, handler);
mGpsGeofenceHardware = gpsGeofence;
+ mFusedGeofenceHardware = fusedGeofenceHardware;
bindHardwareGeofence();
}
@@ -123,6 +129,13 @@
}
}
+ private void setFusedGeofence() {
+ try {
+ mGeofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware);
+ } 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,
@@ -142,6 +155,7 @@
break;
case GEOFENCE_HARDWARE_CONNECTED:
setGpsGeofence();
+ setFusedGeofence();
mGeofenceHardwareConnected = true;
if (mGeofenceProviderConnected) {
setGeofenceHardwareInProvider();
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 38453c8..6053c61 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -24,9 +24,10 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
+import android.hardware.location.GeofenceHardware;
import android.hardware.location.GeofenceHardwareImpl;
-import android.hardware.location.IGeofenceHardware;
import android.location.Criteria;
+import android.location.FusedBatchOptions;
import android.location.IGpsGeofenceHardware;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
@@ -195,6 +196,17 @@
private static final String PROPERTIES_FILE = "/etc/gps.conf";
+ private static final int GPS_GEOFENCE_UNAVAILABLE = 1<<0L;
+ private static final int GPS_GEOFENCE_AVAILABLE = 1<<1L;
+
+ // 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;
+
/** simpler wrapper for ProviderRequest + Worksource */
private static class GpsRequest {
public ProviderRequest request;
@@ -501,7 +513,7 @@
LocationManager locManager =
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
locManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
- 0, 0, new NetworkLocationListener(), mHandler.getLooper());
+ 0, 0, new NetworkLocationListener(), mHandler.getLooper());
}
});
}
@@ -1405,6 +1417,62 @@
}
/**
+ * Helper method to construct a location object.
+ */
+ private Location buildLocation(
+ int flags,
+ double latitude,
+ double longitude,
+ double altitude,
+ float speed,
+ float bearing,
+ float accuracy,
+ long timestamp) {
+ 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);
+ location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
+ }
+ if((flags & LOCATION_HAS_ALTITUDE) == LOCATION_HAS_ALTITUDE) {
+ location.setAltitude(altitude);
+ }
+ if((flags & LOCATION_HAS_SPEED) == LOCATION_HAS_SPEED) {
+ location.setSpeed(speed);
+ }
+ if((flags & LOCATION_HAS_BEARING) == LOCATION_HAS_BEARING) {
+ location.setBearing(bearing);
+ }
+ if((flags & LOCATION_HAS_ACCURACY) == LOCATION_HAS_ACCURACY) {
+ location.setAccuracy(accuracy);
+ }
+ return location;
+ }
+
+ /**
+ * Converts the GPS HAL status to the internal Geofence Hardware status.
+ */
+ 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;
+ default:
+ return -1;
+ }
+ }
+
+ /**
* Called from native to report GPS Geofence transition
* All geofence callbacks are called on the same thread
*/
@@ -1414,8 +1482,22 @@
if (mGeofenceHardwareImpl == null) {
mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
}
- mGeofenceHardwareImpl.reportGpsGeofenceTransition(geofenceId, flags, latitude, longitude,
- altitude, speed, bearing, accuracy, timestamp, transition, transitionTimestamp);
+ Location location = buildLocation(
+ flags,
+ latitude,
+ longitude,
+ altitude,
+ speed,
+ bearing,
+ accuracy,
+ timestamp);
+ mGeofenceHardwareImpl.reportGeofenceTransition(
+ geofenceId,
+ location,
+ transition,
+ transitionTimestamp,
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+ FusedBatchOptions.SourceTechnologies.GNSS);
}
/**
@@ -1427,8 +1509,24 @@
if (mGeofenceHardwareImpl == null) {
mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
}
- mGeofenceHardwareImpl.reportGpsGeofenceStatus(status, flags, latitude, longitude, altitude,
- speed, bearing, accuracy, timestamp);
+ Location location = buildLocation(
+ flags,
+ latitude,
+ longitude,
+ altitude,
+ speed,
+ bearing,
+ accuracy,
+ timestamp);
+ int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
+ if(status == GPS_GEOFENCE_AVAILABLE) {
+ monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
+ }
+ mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
+ GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+ monitorStatus,
+ location,
+ FusedBatchOptions.SourceTechnologies.GNSS);
}
/**
@@ -1438,7 +1536,7 @@
if (mGeofenceHardwareImpl == null) {
mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
}
- mGeofenceHardwareImpl.reportGpsGeofenceAddStatus(geofenceId, status);
+ mGeofenceHardwareImpl.reportGeofenceAddStatus(geofenceId, getGeofenceStatus(status));
}
/**
@@ -1448,7 +1546,7 @@
if (mGeofenceHardwareImpl == null) {
mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
}
- mGeofenceHardwareImpl.reportGpsGeofenceRemoveStatus(geofenceId, status);
+ mGeofenceHardwareImpl.reportGeofenceRemoveStatus(geofenceId, getGeofenceStatus(status));
}
/**
@@ -1458,7 +1556,7 @@
if (mGeofenceHardwareImpl == null) {
mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
}
- mGeofenceHardwareImpl.reportGpsGeofencePauseStatus(geofenceId, status);
+ mGeofenceHardwareImpl.reportGeofencePauseStatus(geofenceId, getGeofenceStatus(status));
}
/**
@@ -1468,7 +1566,7 @@
if (mGeofenceHardwareImpl == null) {
mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
}
- mGeofenceHardwareImpl.reportGpsGeofenceResumeStatus(geofenceId, status);
+ mGeofenceHardwareImpl.reportGeofenceResumeStatus(geofenceId, getGeofenceStatus(status));
}
//=============================================================
diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java
index 6e0e055..db030f1 100644
--- a/services/java/com/android/server/wifi/WifiService.java
+++ b/services/java/com/android/server/wifi/WifiService.java
@@ -25,18 +25,20 @@
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
-import android.net.wifi.IWifiManager;
-import android.net.wifi.ScanResult;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
-import android.net.wifi.WifiStateMachine;
-import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiWatchdogStateMachine;
import android.net.DhcpInfo;
import android.net.DhcpResults;
import android.net.LinkAddress;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.wifi.IWifiManager;
+import android.net.wifi.ScanResult;
+import android.net.wifi.BatchedScanResult;
+import android.net.wifi.BatchedScanSettings;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiStateMachine;
+import android.net.wifi.WifiWatchdogStateMachine;
import android.os.Binder;
import android.os.Handler;
import android.os.Messenger;
@@ -63,6 +65,7 @@
import java.net.InetAddress;
import java.net.Inet4Address;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -121,6 +124,8 @@
/* Tracks the persisted states for wi-fi & airplane mode */
final WifiSettingsStore mSettingsStore;
+ final boolean mBatchedScanSupported;
+
/**
* Asynchronous channel to WifiStateMachine
*/
@@ -246,6 +251,9 @@
mWifiController = new WifiController(mContext, this, wifiThread.getLooper());
mWifiController.start();
+ mBatchedScanSupported = mContext.getResources().getBoolean(
+ R.bool.config_wifi_batched_scan_supported);
+
registerForScanModeChange();
mContext.registerReceiver(
new BroadcastReceiver() {
@@ -314,6 +322,142 @@
mWifiStateMachine.startScan(Binder.getCallingUid(), workSource);
}
+ private class BatchedScanRequest extends DeathRecipient {
+ BatchedScanSettings settings;
+ int uid;
+
+ BatchedScanRequest(BatchedScanSettings settings, IBinder binder, int uid) {
+ super(0, null, binder, null);
+ this.settings = settings;
+ this.uid = uid;
+ }
+ public void binderDied() {
+ stopBatchedScan(settings, mBinder);
+ }
+ public String toString() {
+ return "BatchedScanRequest{settings=" + settings + ", binder=" + mBinder + "}";
+ }
+ }
+
+ private final List<BatchedScanRequest> mBatchedScanners = new ArrayList<BatchedScanRequest>();
+
+ public boolean isBatchedScanSupported() {
+ return mBatchedScanSupported;
+ }
+
+ /**
+ * see {@link android.net.wifi.WifiManager#requestBatchedScan()}
+ */
+ public boolean requestBatchedScan(BatchedScanSettings requested, IBinder binder) {
+ enforceChangePermission();
+ if (mBatchedScanSupported == false) return false;
+ requested = new BatchedScanSettings(requested);
+ if (requested.isInvalid()) return false;
+ BatchedScanRequest r = new BatchedScanRequest(requested, binder, Binder.getCallingUid());
+ synchronized(mBatchedScanners) {
+ mBatchedScanners.add(r);
+ resolveBatchedScannersLocked();
+ }
+ return true;
+ }
+
+ public List<BatchedScanResult> getBatchedScanResults(String callingPackage) {
+ enforceAccessPermission();
+ if (mBatchedScanSupported == false) return new ArrayList<BatchedScanResult>();
+ int userId = UserHandle.getCallingUserId();
+ int uid = Binder.getCallingUid();
+ long ident = Binder.clearCallingIdentity();
+ try {
+ if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
+ != AppOpsManager.MODE_ALLOWED) {
+ return new ArrayList<BatchedScanResult>();
+ }
+ int currentUser = ActivityManager.getCurrentUser();
+ if (userId != currentUser) {
+ return new ArrayList<BatchedScanResult>();
+ } else {
+ return mWifiStateMachine.syncGetBatchedScanResultsList();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+
+ public void stopBatchedScan(BatchedScanSettings settings, IBinder binder) {
+ enforceChangePermission();
+ if (mBatchedScanSupported == false) return;
+ synchronized(mBatchedScanners) {
+ BatchedScanRequest found = null;
+ for (BatchedScanRequest r : mBatchedScanners) {
+ if (r.mBinder.equals(binder) && r.settings.equals(settings)) {
+ found = r;
+ break;
+ }
+ }
+ if (found != null) {
+ mBatchedScanners.remove(found);
+ resolveBatchedScannersLocked();
+ }
+ }
+ }
+
+ private void resolveBatchedScannersLocked() {
+ BatchedScanSettings setting = new BatchedScanSettings();
+ setting.scanIntervalSec = BatchedScanSettings.DEFAULT_INTERVAL_SEC;
+ int responsibleUid = 0;
+ setting.channelSet = new ArrayList<String>();
+
+ if (mBatchedScanners.size() == 0) {
+ mWifiStateMachine.setBatchedScanSettings(null, 0);
+ return;
+ }
+
+ for (BatchedScanRequest r : mBatchedScanners) {
+ BatchedScanSettings s = r.settings;
+ if (s.maxScansPerBatch != BatchedScanSettings.UNSPECIFIED &&
+ s.maxScansPerBatch < setting.maxScansPerBatch) {
+ setting.maxScansPerBatch = s.maxScansPerBatch;
+ responsibleUid = r.uid;
+ }
+ if (s.maxApPerScan != BatchedScanSettings.UNSPECIFIED &&
+ s.maxApPerScan > setting.maxApPerScan) {
+ setting.maxApPerScan = s.maxApPerScan;
+ }
+ if (s.scanIntervalSec != BatchedScanSettings.UNSPECIFIED &&
+ s.scanIntervalSec < setting.scanIntervalSec) {
+ setting.scanIntervalSec = s.scanIntervalSec;
+ responsibleUid = r.uid;
+ }
+ if (s.maxApForDistance != BatchedScanSettings.UNSPECIFIED &&
+ s.maxApForDistance > setting.maxApForDistance) {
+ setting.maxApForDistance = s.maxApForDistance;
+ }
+ if (s.channelSet != null) {
+ for (String i : s.channelSet) {
+ if (setting.channelSet.contains(i) == false) setting.channelSet.add(i);
+ }
+ }
+ }
+ if (setting.channelSet.size() == 0) setting.channelSet = null;
+ if (setting.scanIntervalSec < BatchedScanSettings.MIN_INTERVAL_SEC) {
+ setting.scanIntervalSec = BatchedScanSettings.MIN_INTERVAL_SEC;
+ }
+ if (setting.maxScansPerBatch == BatchedScanSettings.UNSPECIFIED) {
+ setting.maxScansPerBatch = BatchedScanSettings.DEFAULT_SCANS_PER_BATCH;
+ }
+ if (setting.maxApPerScan == BatchedScanSettings.UNSPECIFIED) {
+ setting.maxApPerScan = BatchedScanSettings.DEFAULT_AP_PER_SCAN;
+ }
+ if (setting.scanIntervalSec == BatchedScanSettings.UNSPECIFIED) {
+ setting.scanIntervalSec = BatchedScanSettings.DEFAULT_INTERVAL_SEC;
+ }
+ if (setting.maxApForDistance == BatchedScanSettings.UNSPECIFIED) {
+ setting.maxApForDistance = BatchedScanSettings.DEFAULT_AP_FOR_DISTANCE;
+ }
+ mWifiStateMachine.setBatchedScanSettings(setting, responsibleUid);
+ }
+
private void enforceAccessPermission() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_WIFI_STATE,
"WifiService");
@@ -569,11 +713,11 @@
int userId = UserHandle.getCallingUserId();
int uid = Binder.getCallingUid();
long ident = Binder.clearCallingIdentity();
- if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
- != AppOpsManager.MODE_ALLOWED) {
- return new ArrayList<ScanResult>();
- }
try {
+ if (mAppOps.noteOp(AppOpsManager.OP_WIFI_SCAN, uid, callingPackage)
+ != AppOpsManager.MODE_ALLOWED) {
+ return new ArrayList<ScanResult>();
+ }
int currentUser = ActivityManager.getCurrentUser();
if (userId != currentUser) {
return new ArrayList<ScanResult>();
diff --git a/services/jni/com_android_server_location_FlpHardwareProvider.cpp b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
index 48b86db..c871828 100644
--- a/services/jni/com_android_server_location_FlpHardwareProvider.cpp
+++ b/services/jni/com_android_server_location_FlpHardwareProvider.cpp
@@ -261,6 +261,75 @@
}
/*
+ * Helper function to unwrap Geofence structures from the Java Runtime calls.
+ */
+static void TranslateGeofenceFromGeofenceHardwareRequestParcelable(
+ JNIEnv* env,
+ jobject geofenceRequestObject,
+ Geofence& geofence) {
+ jclass geofenceRequestClass = env->GetObjectClass(geofenceRequestObject);
+
+ jmethodID getId = env->GetMethodID(geofenceRequestClass, "getId", "()I");
+ geofence.geofence_id = env->CallIntMethod(geofenceRequestObject, getId);
+
+ jmethodID getType = env->GetMethodID(geofenceRequestClass, "getType", "()I");
+ // this works because GeofenceHardwareRequest.java and fused_location.h have
+ // the same notion of geofence types
+ GeofenceType type = (GeofenceType)env->CallIntMethod(geofenceRequestObject, getType);
+ if(type != TYPE_CIRCLE) {
+ ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
+ }
+ geofence.data->type = type;
+ GeofenceCircle& circle = geofence.data->geofence.circle;
+
+ jmethodID getLatitude = env->GetMethodID(
+ geofenceRequestClass,
+ "getLatitude",
+ "()D");
+ circle.latitude = env->CallDoubleMethod(geofenceRequestObject, getLatitude);
+
+ jmethodID getLongitude = env->GetMethodID(
+ geofenceRequestClass,
+ "getLongitude",
+ "()D");
+ circle.longitude = env->CallDoubleMethod(geofenceRequestObject, getLongitude);
+
+ jmethodID getRadius = env->GetMethodID(geofenceRequestClass, "getRadius", "()D");
+ circle.radius_m = env->CallDoubleMethod(geofenceRequestObject, getRadius);
+
+ GeofenceOptions* options = geofence.options;
+ jmethodID getMonitorTransitions = env->GetMethodID(
+ geofenceRequestClass,
+ "getMonitorTransitions",
+ "()I");
+ options->monitor_transitions = env->CallIntMethod(
+ geofenceRequestObject,
+ getMonitorTransitions);
+
+ jmethodID getUnknownTimer = env->GetMethodID(
+ geofenceRequestClass,
+ "getUnknownTimer",
+ "()I");
+ options->unknown_timer_ms = env->CallIntMethod(geofenceRequestObject, getUnknownTimer);
+
+ jmethodID getNotificationResponsiveness = env->GetMethodID(
+ geofenceRequestClass,
+ "getNotificationResponsiveness",
+ "()D");
+ options->notification_responsivenes_ms = env->CallIntMethod(
+ geofenceRequestObject,
+ getNotificationResponsiveness);
+
+ jmethodID getLastTransition = env->GetMethodID(
+ geofenceRequestClass,
+ "getLastTransition",
+ "()I");
+ options->last_transition = env->CallIntMethod(geofenceRequestObject, getLastTransition);
+
+ // TODO: set data.sources_to_use when available
+}
+
+/*
* Helper function to transform FlpLocation into a java object.
*/
static void TranslateToObject(const FlpLocation* location, jobject& locationObject) {
@@ -559,7 +628,7 @@
}
err = module->methods->open(
- module,
+ module,
FUSED_LOCATION_HARDWARE_MODULE_ID, &sHardwareDevice);
if(err != 0) {
ALOGE("Error opening device '%s': %d", FUSED_LOCATION_HARDWARE_MODULE_ID, err);
@@ -749,10 +818,9 @@
static void AddGeofences(
JNIEnv* env,
jobject object,
- jintArray geofenceIdsArray,
- jobjectArray geofencesArray) {
- if(geofencesArray == NULL) {
- ALOGE("Invalid Geofences to add: %p", geofencesArray);
+ jobjectArray geofenceRequestsArray) {
+ if(geofenceRequestsArray == NULL) {
+ ALOGE("Invalid Geofences to add: %p", geofenceRequestsArray);
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
}
@@ -760,23 +828,32 @@
ThrowOnError(env, FLP_RESULT_ERROR, __FUNCTION__);
}
- jint geofencesCount = env->GetArrayLength(geofenceIdsArray);
- Geofence* geofences = new Geofence[geofencesCount];
+ jint geofenceRequestsCount = env->GetArrayLength(geofenceRequestsArray);
+ if(geofenceRequestsCount == 0) {
+ return;
+ }
+
+ Geofence* geofences = new Geofence[geofenceRequestsCount];
if (geofences == NULL) {
ThrowOnError(env, FLP_RESULT_INSUFFICIENT_MEMORY, __FUNCTION__);
}
- jint* ids = env->GetIntArrayElements(geofenceIdsArray, /* isCopy */ NULL);
- for (int i = 0; i < geofencesCount; ++i) {
- geofences[i].geofence_id = ids[i];
+ for (int i = 0; i < geofenceRequestsCount; ++i) {
+ geofences[i].data = new GeofenceData();
+ geofences[i].options = new GeofenceOptions();
+ jobject geofenceObject = env->GetObjectArrayElement(geofenceRequestsArray, i);
- // TODO: fill in the GeofenceData
-
- // TODO: fill in the GeofenceOptions
+ TranslateGeofenceFromGeofenceHardwareRequestParcelable(env, geofenceObject, geofences[i]);
}
- sFlpGeofencingInterface->add_geofences(geofencesCount, &geofences);
- if (geofences != NULL) delete[] geofences;
+ sFlpGeofencingInterface->add_geofences(geofenceRequestsCount, &geofences);
+ if (geofences != NULL) {
+ for(int i = 0; i < geofenceRequestsCount; ++i) {
+ delete geofences[i].data;
+ delete geofences[i].options;
+ }
+ delete[] geofences;
+ }
}
static void PauseGeofence(JNIEnv* env, jobject object, jint geofenceId) {
@@ -847,41 +924,41 @@
{"nativeCleanup", "()V", reinterpret_cast<void*>(Cleanup)},
{"nativeIsSupported", "()Z", reinterpret_cast<void*>(IsSupported)},
{"nativeGetBatchSize", "()I", reinterpret_cast<void*>(GetBatchSize)},
- {"nativeStartBatching",
- "(ILandroid/location/FusedBatchOptions;)V",
+ {"nativeStartBatching",
+ "(ILandroid/location/FusedBatchOptions;)V",
reinterpret_cast<void*>(StartBatching)},
- {"nativeUpdateBatchingOptions",
- "(ILandroid/location/FusedBatchOptions;)V",
+ {"nativeUpdateBatchingOptions",
+ "(ILandroid/location/FusedBatchOptions;)V",
reinterpret_cast<void*>(UpdateBatchingOptions)},
{"nativeStopBatching", "(I)V", reinterpret_cast<void*>(StopBatching)},
- {"nativeRequestBatchedLocation",
- "(I)V",
+ {"nativeRequestBatchedLocation",
+ "(I)V",
reinterpret_cast<void*>(GetBatchedLocation)},
- {"nativeInjectLocation",
- "(Landroid/location/Location;)V",
+ {"nativeInjectLocation",
+ "(Landroid/location/Location;)V",
reinterpret_cast<void*>(InjectLocation)},
- {"nativeIsDiagnosticSupported",
- "()Z",
+ {"nativeIsDiagnosticSupported",
+ "()Z",
reinterpret_cast<void*>(IsDiagnosticSupported)},
- {"nativeInjectDiagnosticData",
- "(Ljava/lang/String;)V",
+ {"nativeInjectDiagnosticData",
+ "(Ljava/lang/String;)V",
reinterpret_cast<void*>(InjectDiagnosticData)},
- {"nativeIsDeviceContextSupported",
- "()Z",
+ {"nativeIsDeviceContextSupported",
+ "()Z",
reinterpret_cast<void*>(IsDeviceContextSupported)},
- {"nativeInjectDeviceContext",
- "(I)V",
+ {"nativeInjectDeviceContext",
+ "(I)V",
reinterpret_cast<void*>(InjectDeviceContext)},
- {"nativeIsGeofencingSupported",
- "()Z",
+ {"nativeIsGeofencingSupported",
+ "()Z",
reinterpret_cast<void*>(IsGeofencingSupported)},
- {"nativeAddGeofences",
- "([I[Landroid/location/Geofence;)V",
+ {"nativeAddGeofences",
+ "([Landroid/hardware/location/GeofenceHardwareRequestParcelable;)V",
reinterpret_cast<void*>(AddGeofences)},
{"nativePauseGeofence", "(I)V", reinterpret_cast<void*>(PauseGeofence)},
{"nativeResumeGeofence", "(II)V", reinterpret_cast<void*>(ResumeGeofence)},
- {"nativeModifyGeofenceOption",
- "(IIIIII)V",
+ {"nativeModifyGeofenceOption",
+ "(IIIIII)V",
reinterpret_cast<void*>(ModifyGeofenceOption)},
{"nativeRemoveGeofences", "([I)V", reinterpret_cast<void*>(RemoveGeofences)}
};
diff --git a/wifi/java/android/net/wifi/BatchedScanResult.aidl b/wifi/java/android/net/wifi/BatchedScanResult.aidl
new file mode 100644
index 0000000..a70bc0a
--- /dev/null
+++ b/wifi/java/android/net/wifi/BatchedScanResult.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.net.wifi;
+
+parcelable BatchedScanResult;
diff --git a/wifi/java/android/net/wifi/BatchedScanResult.java b/wifi/java/android/net/wifi/BatchedScanResult.java
new file mode 100644
index 0000000..eb4e0276
--- /dev/null
+++ b/wifi/java/android/net/wifi/BatchedScanResult.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2008 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.net.wifi;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Describes the Results of a batched set of wifi scans where the firmware performs many
+ * scans and stores the timestamped results without waking the main processor each time.
+ * @hide pending review
+ */
+public class BatchedScanResult implements Parcelable {
+ private static final String TAG = "BatchedScanResult";
+
+ /** Inidcates this scan was interrupted and may only have partial results. */
+ public boolean truncated;
+
+ /** The result of this particular scan. */
+ public final List<ScanResult> scanResults = new ArrayList<ScanResult>();
+
+
+ public BatchedScanResult() {
+ }
+
+ public BatchedScanResult(BatchedScanResult source) {
+ truncated = source.truncated;
+ for (ScanResult s : source.scanResults) scanResults.add(new ScanResult(s));
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+
+ sb.append("BatchedScanResult: ").
+ append("truncated: ").append(String.valueOf(truncated)).
+ append("scanResults: [");
+ for (ScanResult s : scanResults) {
+ sb.append(" <").append(s.toString()).append("> ");
+ }
+ sb.append(" ]");
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(truncated ? 1 : 0);
+ dest.writeInt(scanResults.size());
+ for (ScanResult s : scanResults) {
+ s.writeToParcel(dest, flags);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<BatchedScanResult> CREATOR =
+ new Creator<BatchedScanResult>() {
+ public BatchedScanResult createFromParcel(Parcel in) {
+ BatchedScanResult result = new BatchedScanResult();
+ result.truncated = (in.readInt() == 1);
+ int count = in.readInt();
+ while (count-- > 0) {
+ result.scanResults.add(ScanResult.CREATOR.createFromParcel(in));
+ }
+ return result;
+ }
+
+ public BatchedScanResult[] newArray(int size) {
+ return new BatchedScanResult[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/BatchedScanSettings.aidl b/wifi/java/android/net/wifi/BatchedScanSettings.aidl
new file mode 100644
index 0000000..8cfc508
--- /dev/null
+++ b/wifi/java/android/net/wifi/BatchedScanSettings.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.net.wifi;
+
+parcelable BatchedScanSettings;
diff --git a/wifi/java/android/net/wifi/BatchedScanSettings.java b/wifi/java/android/net/wifi/BatchedScanSettings.java
new file mode 100644
index 0000000..82945d6
--- /dev/null
+++ b/wifi/java/android/net/wifi/BatchedScanSettings.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2008 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.net.wifi;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * Describes the settings for batched wifi scans where the firmware performs many
+ * scans and stores the timestamped results without waking the main processor each time.
+ * This can give information over time with minimal battery impact.
+ * @hide pending review
+ */
+public class BatchedScanSettings implements Parcelable {
+ private static final String TAG = "BatchedScanSettings";
+
+ /** Used to indicate no preference for an int value */
+ public final static int UNSPECIFIED = Integer.MAX_VALUE;
+
+ // TODO - make MIN/mAX dynamic and gservices adjustable.
+ public final static int MIN_SCANS_PER_BATCH = 2;
+ public final static int MAX_SCANS_PER_BATCH = 255;
+ public final static int DEFAULT_SCANS_PER_BATCH = MAX_SCANS_PER_BATCH;
+
+ public final static int MIN_AP_PER_SCAN = 2;
+ public final static int MAX_AP_PER_SCAN = 255;
+ public final static int DEFAULT_AP_PER_SCAN = 16;
+
+ public final static int MIN_INTERVAL_SEC = 0;
+ public final static int MAX_INTERVAL_SEC = 3600;
+ public final static int DEFAULT_INTERVAL_SEC = 30;
+
+ public final static int MIN_AP_FOR_DISTANCE = 0;
+ public final static int MAX_AP_FOR_DISTANCE = MAX_AP_PER_SCAN;
+ public final static int DEFAULT_AP_FOR_DISTANCE = 0;
+
+
+ /** The expected number of scans per batch. Note that the firmware may drop scans
+ * leading to fewer scans during the normal batch scan duration. This value need not
+ * be specified (may be set to {@link UNSPECIFIED}) by the application and we will try
+ * to scan as many times as the firmware can support. If another app requests fewer
+ * scans per batch we will attempt to honor that.
+ */
+ public int maxScansPerBatch;
+
+ /** The maximum desired AP listed per scan. Fewer AP may be returned if that's all
+ * that the driver detected. If another application requests more AP per scan that
+ * will take precedence. The if more channels are detected than we request, the APs
+ * with the lowest signal strength will be dropped.
+ */
+ public int maxApPerScan;
+
+ /** The channels used in the scan. If all channels should be used, {@code null} may be
+ * specified. If another application requests more channels or all channels, that
+ * will take precedence.
+ */
+ public Collection<String> channelSet;
+
+ /** The time between the start of two sequential scans, in seconds. If another
+ * application requests more frequent scans, that will take precedence. If this
+ * value is less than the duration of a scan, the next scan should start immediately.
+ */
+ public int scanIntervalSec;
+
+ /** The number of the best (strongest signal) APs for which the firmware will
+ * attempt to get distance information (RTT). Not all firmware supports this
+ * feature, so it may be ignored. If another application requests a greater
+ * number, that will take precedence.
+ */
+ public int maxApForDistance;
+
+ public BatchedScanSettings() {
+ clear();
+ }
+
+ public void clear() {
+ maxScansPerBatch = UNSPECIFIED;
+ maxApPerScan = UNSPECIFIED;
+ channelSet = null;
+ scanIntervalSec = UNSPECIFIED;
+ maxApForDistance = UNSPECIFIED;
+ }
+
+ public BatchedScanSettings(BatchedScanSettings source) {
+ maxScansPerBatch = source.maxScansPerBatch;
+ maxApPerScan = source.maxApPerScan;
+ if (source.channelSet != null) {
+ channelSet = new ArrayList(source.channelSet);
+ }
+ scanIntervalSec = source.scanIntervalSec;
+ maxApForDistance = source.maxApForDistance;
+ }
+
+ private boolean channelSetIsValid() {
+ if (channelSet == null || channelSet.isEmpty()) return true;
+ for (String channel : channelSet) {
+ try {
+ int i = Integer.parseInt(channel);
+ if (i > 0 && i < 197) continue;
+ } catch (NumberFormatException e) {}
+ if (channel.equals("A") || channel.equals("B")) continue;
+ return false;
+ }
+ return true;
+ }
+ /** @hide */
+ public boolean isInvalid() {
+ if (maxScansPerBatch != UNSPECIFIED && (maxScansPerBatch < MIN_SCANS_PER_BATCH ||
+ maxScansPerBatch > MAX_SCANS_PER_BATCH)) return true;
+ if (maxApPerScan != UNSPECIFIED && (maxApPerScan < MIN_AP_PER_SCAN ||
+ maxApPerScan > MAX_AP_PER_SCAN)) return true;
+ if (channelSetIsValid() == false) return true;
+ if (scanIntervalSec != UNSPECIFIED && (scanIntervalSec < MIN_INTERVAL_SEC ||
+ scanIntervalSec > MAX_INTERVAL_SEC)) return true;
+ if (maxApForDistance != UNSPECIFIED && (maxApForDistance < MIN_AP_FOR_DISTANCE ||
+ maxApForDistance > MAX_AP_FOR_DISTANCE)) return true;
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof BatchedScanSettings == false) return false;
+ BatchedScanSettings o = (BatchedScanSettings)obj;
+ if (maxScansPerBatch != o.maxScansPerBatch ||
+ maxApPerScan != o.maxApPerScan ||
+ scanIntervalSec != o.scanIntervalSec ||
+ maxApForDistance != o.maxApForDistance) return false;
+ if (channelSet == null) {
+ return (o.channelSet == null);
+ }
+ return channelSet.equals(o.channelSet);
+ }
+
+ @Override
+ public int hashCode() {
+ return maxScansPerBatch +
+ (maxApPerScan * 3) +
+ (scanIntervalSec * 5) +
+ (maxApForDistance * 7) +
+ (channelSet.hashCode() * 11);
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ String none = "<none>";
+
+ sb.append("BatchScanSettings [maxScansPerBatch: ").
+ append(maxScansPerBatch == UNSPECIFIED ? none : maxScansPerBatch).
+ append(", maxApPerScan: ").append(maxApPerScan == UNSPECIFIED? none : maxApPerScan).
+ append(", scanIntervalSec: ").
+ append(scanIntervalSec == UNSPECIFIED ? none : scanIntervalSec).
+ append(", maxApForDistance: ").
+ append(maxApForDistance == UNSPECIFIED ? none : maxApForDistance).
+ append(", channelSet: ");
+ if (channelSet == null) {
+ sb.append("ALL");
+ } else {
+ sb.append("<");
+ for (String channel : channelSet) {
+ sb.append(" " + channel);
+ }
+ sb.append(">");
+ }
+ sb.append("]");
+ return sb.toString();
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(maxScansPerBatch);
+ dest.writeInt(maxApPerScan);
+ dest.writeInt(scanIntervalSec);
+ dest.writeInt(maxApForDistance);
+ dest.writeInt(channelSet == null ? 0 : channelSet.size());
+ if (channelSet != null) {
+ for (String channel : channelSet) dest.writeString(channel);
+ }
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<BatchedScanSettings> CREATOR =
+ new Creator<BatchedScanSettings>() {
+ public BatchedScanSettings createFromParcel(Parcel in) {
+ BatchedScanSettings settings = new BatchedScanSettings();
+ settings.maxScansPerBatch = in.readInt();
+ settings.maxApPerScan = in.readInt();
+ settings.scanIntervalSec = in.readInt();
+ settings.maxApForDistance = in.readInt();
+ int channelCount = in.readInt();
+ if (channelCount > 0) {
+ settings.channelSet = new ArrayList(channelCount);
+ while (channelCount-- > 0) {
+ settings.channelSet.add(in.readString());
+ }
+ }
+ return settings;
+ }
+
+ public BatchedScanSettings[] newArray(int size) {
+ return new BatchedScanSettings[size];
+ }
+ };
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 8103e84..c8cf323 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -16,8 +16,10 @@
package android.net.wifi;
-import android.net.wifi.WifiInfo;
+import android.net.wifi.BatchedScanResult;
+import android.net.wifi.BatchedScanSettings;
import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
import android.net.wifi.ScanResult;
import android.net.DhcpInfo;
@@ -114,5 +116,13 @@
void enableTdls(String remoteIPAddress, boolean enable);
void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable);
+
+ boolean requestBatchedScan(in BatchedScanSettings requested, IBinder binder);
+
+ void stopBatchedScan(in BatchedScanSettings requested, IBinder binder);
+
+ List<BatchedScanResult> getBatchedScanResults(String callingPackage);
+
+ boolean isBatchedScanSupported();
}
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 9977419..12729d2 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -54,7 +54,26 @@
* Time Synchronization Function (tsf) timestamp in microseconds when
* this result was last seen.
*/
- public long timestamp;
+ public long timestamp;
+
+ /**
+ * The approximate distance to the AP in centimeter, if available. Else
+ * {@link UNSPECIFIED}.
+ * {@hide}
+ */
+ public int distanceCm;
+
+ /**
+ * The standard deviation of the distance to the AP, if available.
+ * Else {@link UNSPECIFIED}.
+ * {@hide}
+ */
+ public int distanceSdCm;
+
+ /**
+ * {@hide}
+ */
+ public final static int UNSPECIFIED = -1;
/** {@hide} */
public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency,
@@ -66,8 +85,23 @@
this.level = level;
this.frequency = frequency;
this.timestamp = tsf;
+ this.distanceCm = UNSPECIFIED;
+ this.distanceSdCm = UNSPECIFIED;
}
+ /** {@hide} */
+ public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency,
+ long tsf, int distCm, int distSdCm) {
+ this.wifiSsid = wifiSsid;
+ this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
+ this.BSSID = BSSID;
+ this.capabilities = caps;
+ this.level = level;
+ this.frequency = frequency;
+ this.timestamp = tsf;
+ this.distanceCm = distCm;
+ this.distanceSdCm = distSdCm;
+ }
/** copy constructor {@hide} */
public ScanResult(ScanResult source) {
@@ -79,6 +113,8 @@
level = source.level;
frequency = source.frequency;
timestamp = source.timestamp;
+ distanceCm = source.distanceCm;
+ distanceSdCm = source.distanceSdCm;
}
}
@@ -100,6 +136,11 @@
append(", timestamp: ").
append(timestamp);
+ sb.append(", distance: ").append((distanceCm != UNSPECIFIED ? distanceCm : "?")).
+ append("(cm)");
+ sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")).
+ append("(cm)");
+
return sb.toString();
}
@@ -121,6 +162,8 @@
dest.writeInt(level);
dest.writeInt(frequency);
dest.writeLong(timestamp);
+ dest.writeInt(distanceCm);
+ dest.writeInt(distanceSdCm);
}
/** Implement the Parcelable interface {@hide} */
@@ -137,7 +180,9 @@
in.readString(),
in.readInt(),
in.readInt(),
- in.readLong()
+ in.readLong(),
+ in.readInt(),
+ in.readInt()
);
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6793710..01ca378 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -35,6 +35,7 @@
import java.net.InetAddress;
import java.util.concurrent.CountDownLatch;
+import com.android.internal.R;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
@@ -365,6 +366,14 @@
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
/**
+ * A batch of access point scans has been completed and the results areavailable.
+ * Call {@link #getBatchedScanResults()} to obtain the results.
+ * @hide pending review
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String BATCHED_SCAN_RESULTS_AVAILABLE_ACTION =
+ "android.net.wifi.BATCHED_RESULTS";
+ /**
* The RSSI (signal strength) has changed.
* @see #EXTRA_NEW_RSSI
*/
@@ -778,6 +787,59 @@
}
/**
+ * Request a batched scan for access points. To end your requested batched scan,
+ * call stopBatchedScan with the same Settings.
+ *
+ * If there are mulitple requests for batched scans, the more demanding settings will
+ * take precidence.
+ *
+ * @param requested {@link BatchedScanSettings} the scan settings requested.
+ * @return false on known error
+ * @hide
+ */
+ public boolean requestBatchedScan(BatchedScanSettings requested) {
+ try {
+ return mService.requestBatchedScan(requested, new Binder());
+ } catch (RemoteException e) { return false; }
+ }
+
+ /**
+ * Check if the Batched Scan feature is supported.
+ *
+ * @return false if not supported.
+ * @hide
+ */
+ public boolean isBatchedScanSupported() {
+ try {
+ return mService.isBatchedScanSupported();
+ } catch (RemoteException e) { return false; }
+ }
+
+ /**
+ * End a requested batch scan for this applicaiton. Note that batched scan may
+ * still occur if other apps are using them.
+ * @hide
+ */
+ public void stopBatchedScan(BatchedScanSettings requested) {
+ try {
+ mService.stopBatchedScan(requested, new Binder());
+ } catch (RemoteException e) {}
+ }
+
+ /**
+ * Retrieve the latest batched scan result. This should be called immediately after
+ * {@link BATCHED_SCAN_RESULTS_AVAILABLE_ACTION} is received.
+ * @hide
+ */
+ public List<BatchedScanResult> getBatchedScanResults() {
+ try {
+ return mService.getBatchedScanResults(mContext.getBasePackageName());
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
* Return dynamic information about the current Wi-Fi connection, if any is active.
* @return the Wi-Fi information, contained in {@link WifiInfo}.
*/
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index d30c7cf..0359076 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -219,6 +219,40 @@
return doStringCommand("BSS RANGE=" + sid + "- MASK=0x21987");
}
+ /**
+ * Format of command
+ * DRIVER WLS_BATCHING SET SCAN_FRQ=x BESTN=y CHANNEL=<z, w, t> RTT=s
+ * where x is an ascii representation of an integer number of seconds between scans
+ * y is an ascii representation of an integer number of the max AP to remember per scan
+ * z, w, t represent a 1..n size list of channel numbers and/or 'A', 'B' values
+ * indicating entire ranges of channels
+ * s is an ascii representation of an integer number of highest-strength AP
+ * for which we'd like approximate distance reported
+ *
+ * The return value is an ascii integer representing a guess of the number of scans
+ * the firmware can remember before it runs out of buffer space or -1 on error
+ */
+ public String setBatchedScanSettings(BatchedScanSettings settings) {
+ if (settings == null) return doStringCommand("DRIVER WLS_BATCHING STOP");
+ String cmd = "DRIVER WLS_BATCHING SET SCAN_FRQ=" + settings.scanIntervalSec;
+ if (settings.maxApPerScan != BatchedScanSettings.UNSPECIFIED) {
+ cmd += " BESTN " + settings.maxApPerScan;
+ }
+ if (settings.channelSet != null && !settings.channelSet.isEmpty()) {
+ cmd += " CHANNEL=<";
+ for (String channel : settings.channelSet) cmd += " " + channel;
+ cmd += ">";
+ }
+ if (settings.maxApForDistance != BatchedScanSettings.UNSPECIFIED) {
+ cmd += " RTT=" + settings.maxApForDistance;
+ }
+ return doStringCommand(cmd);
+ }
+
+ public String getBatchedScanResults() {
+ return doStringCommand("DRIVER WLS_BATCHING GET");
+ }
+
public boolean startDriver() {
return doBooleanCommand("DRIVER START");
}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 1fcd609..764c00a 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -123,6 +123,11 @@
private static final int SCAN_RESULT_CACHE_SIZE = 80;
private final LruCache<String, ScanResult> mScanResultCache;
+ /* Batch scan results */
+ private final List<BatchedScanResult> mBatchedScanResults =
+ new ArrayList<BatchedScanResult>();
+ private int mBatchedScanOwnerUid = UNKNOWN_SCAN_SOURCE;
+
/* Chipset supports background scan */
private final boolean mBackgroundScanSupported;
@@ -211,6 +216,7 @@
private AlarmManager mAlarmManager;
private PendingIntent mScanIntent;
private PendingIntent mDriverStopIntent;
+ private PendingIntent mBatchedScanIntervalIntent;
/* Tracks current frequency mode */
private AtomicInteger mFrequencyBand = new AtomicInteger(WifiManager.WIFI_FREQUENCY_BAND_AUTO);
@@ -356,6 +362,13 @@
public static final int CMD_BOOT_COMPLETED = BASE + 134;
+ /* change the batch scan settings.
+ * arg1 = responsible UID
+ * obj = the new settings
+ */
+ public static final int CMD_SET_BATCH_SCAN = BASE + 135;
+ public static final int CMD_START_NEXT_BATCHED_SCAN = BASE + 136;
+
public static final int CONNECT_MODE = 1;
public static final int SCAN_ONLY_MODE = 2;
public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE = 3;
@@ -520,6 +533,8 @@
private static final String ACTION_DELAYED_DRIVER_STOP =
"com.android.server.WifiManager.action.DELAYED_DRIVER_STOP";
+ private static final String ACTION_REFRESH_BATCHED_SCAN =
+ "com.android.server.WifiManager.action.REFRESH_BATCHED_SCAN";
/**
* Keep track of whether WIFI is running.
*/
@@ -542,6 +557,9 @@
private final IBatteryStats mBatteryStats;
+ private BatchedScanSettings mBatchedScanSettings = null;
+
+
public WifiStateMachine(Context context, String wlanInterface) {
super("WifiStateMachine");
mContext = context;
@@ -577,6 +595,9 @@
Intent scanIntent = new Intent(ACTION_START_SCAN, null);
mScanIntent = PendingIntent.getBroadcast(mContext, SCAN_REQUEST, scanIntent, 0);
+ Intent batchedIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
+ mBatchedScanIntervalIntent = PendingIntent.getBroadcast(mContext, 0, batchedIntent, 0);
+
mDefaultFrameworkScanIntervalMs = mContext.getResources().getInteger(
R.integer.config_wifi_framework_scan_interval);
@@ -614,22 +635,25 @@
},
new IntentFilter(ACTION_START_SCAN));
- IntentFilter screenFilter = new IntentFilter();
- screenFilter.addAction(Intent.ACTION_SCREEN_ON);
- screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
- BroadcastReceiver screenReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
+ mContext.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
- if (action.equals(Intent.ACTION_SCREEN_ON)) {
- handleScreenStateChanged(true);
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
- handleScreenStateChanged(false);
- }
- }
- };
- mContext.registerReceiver(screenReceiver, screenFilter);
+ if (action.equals(Intent.ACTION_SCREEN_ON)) {
+ handleScreenStateChanged(true);
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+ handleScreenStateChanged(false);
+ } else if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
+ startNextBatchedScanAsync();
+ }
+ }
+ }, filter);
mContext.registerReceiver(
new BroadcastReceiver() {
@@ -738,6 +762,269 @@
sendMessage(CMD_START_SCAN, callingUid, 0, workSource);
}
+ /**
+ * start or stop batched scanning using the given settings
+ */
+ public void setBatchedScanSettings(BatchedScanSettings settings, int callingUid) {
+ sendMessage(CMD_SET_BATCH_SCAN, callingUid, 0, settings);
+ }
+
+ public List<BatchedScanResult> syncGetBatchedScanResultsList() {
+ synchronized (mBatchedScanResults) {
+ List<BatchedScanResult> batchedScanList =
+ new ArrayList<BatchedScanResult>(mBatchedScanResults.size());
+ for(BatchedScanResult result: mBatchedScanResults) {
+ batchedScanList.add(new BatchedScanResult(result));
+ }
+ return batchedScanList;
+ }
+ }
+
+ private void startBatchedScan() {
+ // first grab any existing data
+ retrieveBatchedScanData();
+
+ mAlarmManager.cancel(mBatchedScanIntervalIntent);
+
+ String scansExpected = mWifiNative.setBatchedScanSettings(mBatchedScanSettings);
+
+ try {
+ int expected = Integer.parseInt(scansExpected);
+ setNextBatchedAlarm(expected);
+ } catch (NumberFormatException e) {
+ loge("Exception parsing WifiNative.setBatchedScanSettings response " + e);
+ }
+ }
+
+ // called from BroadcastListener
+ private void startNextBatchedScanAsync() {
+ sendMessage(CMD_START_NEXT_BATCHED_SCAN);
+ }
+
+ private void startNextBatchedScan() {
+ // first grab any existing data
+ int nextCount = retrieveBatchedScanData();
+
+ setNextBatchedAlarm(nextCount);
+ }
+
+ // return true if new/different
+ private boolean recordBatchedScanSettings(BatchedScanSettings settings) {
+ if (DBG) log("set batched scan to " + settings);
+ if (settings != null) {
+ // TODO - noteBatchedScanStart(message.arg1);
+ if (settings.equals(mBatchedScanSettings)) return false;
+ } else {
+ if (mBatchedScanSettings == null) return false;
+ // TODO - noteBatchedScanStop(message.arg1);
+ }
+ mBatchedScanSettings = settings;
+ return true;
+ }
+
+ private void stopBatchedScan() {
+ mAlarmManager.cancel(mBatchedScanIntervalIntent);
+ retrieveBatchedScanData();
+ mWifiNative.setBatchedScanSettings(null);
+ }
+
+ private void setNextBatchedAlarm(int scansExpected) {
+
+ if (mBatchedScanSettings == null || scansExpected < 1) return;
+
+ if (mBatchedScanSettings.maxScansPerBatch < scansExpected) {
+ scansExpected = mBatchedScanSettings.maxScansPerBatch;
+ }
+
+ int secToFull = mBatchedScanSettings.scanIntervalSec;
+ secToFull *= scansExpected;
+
+ int debugPeriod = SystemProperties.getInt("wifi.batchedScan.pollPeriod", 0);
+ if (debugPeriod > 0) secToFull = debugPeriod;
+
+ // set the alarm to do the next poll. We set it a little short as we'd rather
+ // wake up wearly than miss a scan due to buffer overflow
+ mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()
+ + ((secToFull - (mBatchedScanSettings.scanIntervalSec / 2)) * 1000),
+ mBatchedScanIntervalIntent);
+ }
+
+ /**
+ * Start reading new scan data
+ * Data comes in as:
+ * "scancount=5\n"
+ * "nextcount=5\n"
+ * "apcount=3\n"
+ * "trunc\n" (optional)
+ * "bssid=...\n"
+ * "ssid=...\n"
+ * "freq=...\n" (in Mhz)
+ * "level=...\n"
+ * "dist=...\n" (in cm)
+ * "distsd=...\n" (standard deviation, in cm)
+ * "===="
+ * "bssid=...\n"
+ * etc
+ * "===="
+ * "bssid=...\n"
+ * etc
+ * "%%%%"
+ * "apcount=2\n"
+ * "bssid=...\n"
+ * etc
+ * "%%%%
+ * etc
+ * "----"
+ */
+ private int retrieveBatchedScanData() {
+ String rawData = mWifiNative.getBatchedScanResults();
+ if (rawData == null) {
+ loge("Unexpected null BatchedScanResults");
+ return 0;
+ }
+
+ int nextCount = 0;
+ int scanCount = 0;
+ final String END_OF_SCAN = "====";
+ final String END_OF_BATCH = "%%%%";
+ final String END_OF_BATCHES = "----";
+ final String SCANCOUNT = "scancount=";
+ final String NEXTCOUNT = "nextcount=";
+ final String TRUNCATED = "trunc";
+ final String APCOUNT = "apcount=";
+ final String AGE = "age=";
+ final String DIST = "dist=";
+ final String DISTSD = "distsd=";
+
+ String splitData[] = rawData.split("\n");
+ int n = 0;
+ if (splitData[n].startsWith(SCANCOUNT)) {
+ try {
+ scanCount = Integer.parseInt(splitData[n++].substring(SCANCOUNT.length()));
+ } catch (NumberFormatException e) {}
+ }
+ if (scanCount == 0) {
+ loge("scanCount not found");
+ return 0;
+ }
+ if (splitData[n].startsWith(NEXTCOUNT)) {
+ try {
+ nextCount = Integer.parseInt(splitData[n++].substring(NEXTCOUNT.length()));
+ } catch (NumberFormatException e) {}
+ }
+ if (nextCount == 0) {
+ loge("nextCount not found");
+ return 0;
+ }
+
+ final Intent intent = new Intent(WifiManager.BATCHED_SCAN_RESULTS_AVAILABLE_ACTION);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+ synchronized (mBatchedScanResults) {
+ mBatchedScanResults.clear();
+ BatchedScanResult batchedScanResult = new BatchedScanResult();
+
+ String bssid = null;
+ WifiSsid wifiSsid = null;
+ int level = 0;
+ int freq = 0;
+ int dist, distSd;
+ long tsf = 0;
+ dist = distSd = ScanResult.UNSPECIFIED;
+ long now = System.currentTimeMillis();
+
+ while (true) {
+ while (n < splitData.length) {
+ if (splitData[n].equals(END_OF_BATCHES)) {
+ if (++n != splitData.length) {
+ loge("didn't consume " + (splitData.length-n));
+ }
+ if (mBatchedScanResults.size() > 0) {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ return nextCount;
+ }
+ if ((splitData[n].equals(END_OF_SCAN)) || splitData[n].equals(END_OF_BATCH)) {
+ if (bssid != null) {
+ batchedScanResult.scanResults.add(new ScanResult(
+ wifiSsid, bssid, "", level, freq, tsf, dist, distSd));
+ wifiSsid = null;
+ bssid = null;
+ level = 0;
+ freq = 0;
+ tsf = 0;
+ dist = distSd = ScanResult.UNSPECIFIED;
+ }
+ if (splitData[n].equals(END_OF_BATCH)) {
+ if (batchedScanResult.scanResults.size() != 0) {
+ mBatchedScanResults.add(batchedScanResult);
+ batchedScanResult = new BatchedScanResult();
+ } else {
+ logd("Found empty batch");
+ }
+ }
+ n++;
+ } else if (splitData[n].equals(BSSID_STR)) {
+ bssid = splitData[n++].substring(BSSID_STR.length());
+ } else if (splitData[n].equals(FREQ_STR)) {
+ try {
+ freq = Integer.parseInt(splitData[n++].substring(FREQ_STR.length()));
+ } catch (NumberFormatException e) {
+ loge("Invalid freqency: " + splitData[n-1]);
+ freq = 0;
+ }
+ } else if (splitData[n].equals(AGE)) {
+ try {
+ tsf = now - Long.parseLong(splitData[n++].substring(AGE.length()));
+ } catch (NumberFormatException e) {
+ loge("Invalid timestamp: " + splitData[n-1]);
+ tsf = 0;
+ }
+ } else if (splitData[n].equals(SSID_STR)) {
+ wifiSsid = WifiSsid.createFromAsciiEncoded(
+ splitData[n++].substring(SSID_STR.length()));
+ } else if (splitData[n].equals(LEVEL_STR)) {
+ try {
+ level = Integer.parseInt(splitData[n++].substring(LEVEL_STR.length()));
+ if (level > 0) level -= 256;
+ } catch (NumberFormatException e) {
+ loge("Invalid level: " + splitData[n-1]);
+ level = 0;
+ }
+ } else if (splitData[n].equals(DIST)) {
+ try {
+ dist = Integer.parseInt(splitData[n++].substring(DIST.length()));
+ } catch (NumberFormatException e) {
+ loge("Invalid distance: " + splitData[n-1]);
+ dist = ScanResult.UNSPECIFIED;
+ }
+ } else if (splitData[n].equals(DISTSD)) {
+ try {
+ distSd = Integer.parseInt(splitData[n++].substring(DISTSD.length()));
+ } catch (NumberFormatException e) {
+ loge("Invalid distanceSd: " + splitData[n-1]);
+ distSd = ScanResult.UNSPECIFIED;
+ }
+ }
+ }
+ rawData = mWifiNative.getBatchedScanResults();
+ if (rawData == null) {
+ loge("Unexpected null BatchedScanResults");
+ return nextCount;
+ }
+ splitData = rawData.split("\n");
+ if (splitData.length == 0 || splitData[0].equals("ok")) {
+ loge("batch scan results just ended!");
+ if (mBatchedScanResults.size() > 0) {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ return nextCount;
+ }
+ n = 0;
+ }
+ }
+ }
+
// If workSource is not null, blame is given to it, otherwise blame is given to callingUid.
private void noteScanStart(int callingUid, WorkSource workSource) {
if (mScanWorkSource == null && (callingUid != UNKNOWN_SCAN_SOURCE || workSource != null)) {
@@ -1979,6 +2266,12 @@
sendMessageAtFrontOfQueue(CMD_SET_COUNTRY_CODE, countryCode);
}
break;
+ case CMD_SET_BATCH_SCAN:
+ recordBatchedScanSettings((BatchedScanSettings)message.obj);
+ break;
+ case CMD_START_NEXT_BATCHED_SCAN:
+ startNextBatchedScan();
+ break;
/* Discard */
case CMD_START_SCAN:
case CMD_START_SUPPLICANT:
@@ -2470,6 +2763,10 @@
mWifiNative.stopFilteringMulticastV4Packets();
}
+ if (mBatchedScanSettings != null) {
+ startBatchedScan();
+ }
+
if (mOperationalMode != CONNECT_MODE) {
mWifiNative.disconnect();
transitionTo(mScanModeState);
@@ -2511,6 +2808,10 @@
noteScanStart(message.arg1, (WorkSource) message.obj);
startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
break;
+ case CMD_SET_BATCH_SCAN:
+ recordBatchedScanSettings((BatchedScanSettings)message.obj);
+ startBatchedScan();
+ break;
case CMD_SET_COUNTRY_CODE:
String country = (String) message.obj;
if (DBG) log("set country code " + country);
@@ -2639,6 +2940,10 @@
updateBatteryWorkSource(null);
mScanResults = new ArrayList<ScanResult>();
+ if (mBatchedScanSettings != null) {
+ stopBatchedScan();
+ }
+
final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_DISABLED);