GNSS Batching - Default Implementation
Connections from .hal layer, through to
Location Manager System APIs
Bug: 31974439
Test: builds, boots, ordinary GNSS & new GNSS batching
basic functional checks on Marlin
Change-Id: If75118c27b5ed34cfc16c9f817b60a3be5485095
diff --git a/Android.mk b/Android.mk
index 22323c5..59a00a1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -383,6 +383,7 @@
core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
keystore/java/android/security/IKeyChainAliasCallback.aidl \
keystore/java/android/security/IKeyChainService.aidl \
+ location/java/android/location/IBatchedLocationCallback.aidl \
location/java/android/location/ICountryDetector.aidl \
location/java/android/location/ICountryListener.aidl \
location/java/android/location/IFusedProvider.aidl \
diff --git a/api/system-current.txt b/api/system-current.txt
index a0876e1..02b3517 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -21319,6 +21319,11 @@
field public static final android.os.Parcelable.Creator<android.location.Address> CREATOR;
}
+ public abstract class BatchedLocationCallback {
+ ctor public BatchedLocationCallback();
+ method public void onLocationBatch(java.util.List<android.location.Location>);
+ }
+
public class Criteria implements android.os.Parcelable {
ctor public Criteria();
ctor public Criteria(android.location.Criteria);
@@ -21857,14 +21862,17 @@
method public void clearTestProviderEnabled(java.lang.String);
method public void clearTestProviderLocation(java.lang.String);
method public void clearTestProviderStatus(java.lang.String);
+ method public void flushGnssBatch();
method public java.util.List<java.lang.String> getAllProviders();
method public java.lang.String getBestProvider(android.location.Criteria, boolean);
+ method public int getGnssBatchSize();
method public deprecated android.location.GpsStatus getGpsStatus(android.location.GpsStatus);
method public android.location.Location getLastKnownLocation(java.lang.String);
method public android.location.LocationProvider getProvider(java.lang.String);
method public java.util.List<java.lang.String> getProviders(boolean);
method public java.util.List<java.lang.String> getProviders(android.location.Criteria, boolean);
method public boolean isProviderEnabled(java.lang.String);
+ method public boolean registerGnssBatchedLocationCallback(long, boolean, android.location.BatchedLocationCallback, android.os.Handler);
method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
method public boolean registerGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback, android.os.Handler);
method public boolean registerGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
@@ -21895,6 +21903,7 @@
method public void setTestProviderEnabled(java.lang.String, boolean);
method public void setTestProviderLocation(java.lang.String, android.location.Location);
method public void setTestProviderStatus(java.lang.String, int, android.os.Bundle, long);
+ method public boolean unregisterGnssBatchedLocationCallback(android.location.BatchedLocationCallback);
method public void unregisterGnssMeasurementsCallback(android.location.GnssMeasurementsEvent.Callback);
method public void unregisterGnssNavigationMessageCallback(android.location.GnssNavigationMessage.Callback);
method public void unregisterGnssStatusCallback(android.location.GnssStatus.Callback);
diff --git a/location/java/android/location/BatchedLocationCallback.java b/location/java/android/location/BatchedLocationCallback.java
new file mode 100644
index 0000000..f1c40ae
--- /dev/null
+++ b/location/java/android/location/BatchedLocationCallback.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Used for receiving notifications from the LocationManager when
+ * the a batch of location is ready. These methods are called if the
+ * BatchedLocationCallback has been registered with the location manager service
+ * using the
+ * {@link LocationManager#registerGnssBatchedLocationCallback#startGnssBatch(long,
+ * boolean, BatchedLocationCallback, android.os.Handler)} method.
+ * @hide
+ */
+@SystemApi
+public abstract class BatchedLocationCallback {
+
+ /**
+ * Called when a new batch of locations is ready
+ *
+ * @param locations A list of all new locations (possibly zero of them.)
+ */
+ public void onLocationBatch(List<Location> locations) {}
+}
diff --git a/location/java/android/location/BatchedLocationCallbackTransport.java b/location/java/android/location/BatchedLocationCallbackTransport.java
new file mode 100644
index 0000000..e00f855
--- /dev/null
+++ b/location/java/android/location/BatchedLocationCallbackTransport.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.location;
+
+import android.content.Context;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * A handler class to manage transport callbacks for {@link BatchedLocationCallback}.
+ *
+ * @hide
+ */
+class BatchedLocationCallbackTransport
+ extends LocalListenerHelper<BatchedLocationCallback> {
+ private final ILocationManager mLocationManager;
+
+ private final IBatchedLocationCallback mCallbackTransport = new CallbackTransport();
+
+ public BatchedLocationCallbackTransport(Context context, ILocationManager locationManager) {
+ super(context, "BatchedLocationCallbackTransport");
+ mLocationManager = locationManager;
+ }
+
+ @Override
+ protected boolean registerWithServer() throws RemoteException {
+ return mLocationManager.addGnssBatchingCallback(
+ mCallbackTransport,
+ getContext().getPackageName());
+ }
+
+ @Override
+ protected void unregisterFromServer() throws RemoteException {
+ mLocationManager.removeGnssBatchingCallback();
+ }
+
+ private class CallbackTransport extends IBatchedLocationCallback.Stub {
+ @Override
+ public void onLocationBatch(final List<Location> locations) {
+ ListenerOperation<BatchedLocationCallback> operation =
+ new ListenerOperation<BatchedLocationCallback>() {
+ @Override
+ public void execute(BatchedLocationCallback callback)
+ throws RemoteException {
+ callback.onLocationBatch(locations);
+ }
+ };
+ foreach(operation);
+ }
+ }
+}
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index aa26111..c7188aa 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -89,7 +89,7 @@
*/
public static abstract class Callback {
/**
- * The status of GNSS measurements event.
+ * The status of GNSS Navigation Message event.
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
diff --git a/location/java/android/location/IBatchedLocationCallback.aidl b/location/java/android/location/IBatchedLocationCallback.aidl
new file mode 100644
index 0000000..dce9f96
--- /dev/null
+++ b/location/java/android/location/IBatchedLocationCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.location;
+
+import android.location.Location;
+
+import java.util.List;
+
+/**
+ * {@hide}
+ */
+oneway interface IBatchedLocationCallback
+{
+ void onLocationBatch(in List<Location> locations);
+}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index bc8b0262..fc31f32 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -21,6 +21,7 @@
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.Geofence;
+import android.location.IBatchedLocationCallback;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssStatusListener;
import android.location.IGnssNavigationMessageListener;
@@ -71,6 +72,13 @@
int getGnssYearOfHardware();
+ int getGnssBatchSize(String packageName);
+ boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName);
+ void removeGnssBatchingCallback();
+ boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName);
+ void flushGnssBatch(String packageName);
+ boolean stopGnssBatch();
+
// --- deprecated ---
List<String> getAllProviders();
List<String> getProviders(in Criteria criteria, boolean enabledOnly);
@@ -99,6 +107,9 @@
// it need not be shared with other providers.
void reportLocation(in Location location, boolean passive);
+ // Used when a (initially Gnss) Location batch arrives
+ void reportLocationBatch(in List<Location> locations);
+
// for reporting callback completion
void locationCallbackFinished(ILocationListener listener);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 4e14626..f9385c6 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -18,6 +18,7 @@
import com.android.internal.location.ProviderProperties;
+import android.Manifest;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -59,13 +60,15 @@
* return location results, but the update rate will be throttled and the exact
* location will be obfuscated to a coarse level of accuracy.
*/
-public class LocationManager {
+public class LocationManager
+{
private static final String TAG = "LocationManager";
private final Context mContext;
private final ILocationManager mService;
private final GnssMeasurementCallbackTransport mGnssMeasurementCallbackTransport;
private final GnssNavigationMessageCallbackTransport mGnssNavigationMessageCallbackTransport;
+ private final BatchedLocationCallbackTransport mBatchedLocationCallbackTransport;
private final HashMap<GpsStatus.Listener, GnssStatusListenerTransport> mGpsStatusListeners =
new HashMap<>();
private final HashMap<GpsStatus.NmeaListener, GnssStatusListenerTransport> mGpsNmeaListeners =
@@ -321,9 +324,13 @@
public LocationManager(Context context, ILocationManager service) {
mService = service;
mContext = context;
- mGnssMeasurementCallbackTransport = new GnssMeasurementCallbackTransport(mContext, mService);
+ mGnssMeasurementCallbackTransport =
+ new GnssMeasurementCallbackTransport(mContext, mService);
mGnssNavigationMessageCallbackTransport =
new GnssNavigationMessageCallbackTransport(mContext, mService);
+ mBatchedLocationCallbackTransport =
+ new BatchedLocationCallbackTransport(mContext, mService);
+
}
private LocationProvider createProvider(String name, ProviderProperties properties) {
@@ -1878,7 +1885,8 @@
* No-op method to keep backward-compatibility.
* Don't use it. Use {@link #unregisterGnssNavigationMessageCallback} instead.
* @hide
- * @deprecated use {@link #unregisterGnssNavigationMessageCallback(GnssMeasurements.Callback)}
+ * @deprecated use
+ * {@link #unregisterGnssNavigationMessageCallback(GnssNavigationMessage.Callback)}
* instead
*/
@Deprecated
@@ -1960,6 +1968,96 @@
}
/**
+ * Returns the batch size (in number of Location objects) that are supported by the batching
+ * interface.
+ *
+ * @return Maximum number of location objects that can be returned
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public int getGnssBatchSize() {
+ try {
+ return mService.getGnssBatchSize(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Start hardware-batching of GNSS locations. This API is primarily used when the AP is
+ * asleep and the device can batch GNSS locations in the hardware.
+ *
+ * Note this is designed (as was the fused location interface before it) for a single user
+ * SystemApi - requests are not consolidated. Care should be taken when the System switches
+ * users that may have different batching requests, to stop hardware batching for one user, and
+ * restart it for the next.
+ *
+ * @param periodNanos Time interval, in nanoseconds, that the GNSS locations are requested
+ * within the batch
+ * @param wakeOnFifoFull True if the hardware batching should flush the locations in a
+ * a callback to the listener, when it's internal buffer is full. If
+ * set to false, the oldest location information is, instead,
+ * dropped when the buffer is full.
+ * @param callback The listener on which to return the batched locations
+ * @param handler The handler on which to process the callback
+ *
+ * @return True if batching was successfully started
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public boolean registerGnssBatchedLocationCallback(long periodNanos, boolean wakeOnFifoFull,
+ BatchedLocationCallback callback, Handler handler) {
+ mBatchedLocationCallbackTransport.add(callback, handler);
+
+ try {
+ return mService.startGnssBatch(periodNanos, wakeOnFifoFull, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Flush the batched GNSS locations.
+ * All GNSS locations currently ready in the batch are returned via the callback sent in
+ * startGnssBatch(), and the buffer containing the batched locations is cleared.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void flushGnssBatch() {
+ try {
+ mService.flushGnssBatch(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Stop batching locations. This API is primarily used when the AP is
+ * asleep and the device can batch locations in the hardware.
+ *
+ * @param callback the specific callback class to remove from the transport layer
+ *
+ * @return True if batching was successfully started
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public boolean unregisterGnssBatchedLocationCallback(BatchedLocationCallback callback) {
+
+ mBatchedLocationCallbackTransport.remove(callback);
+
+ try {
+ return mService.stopGnssBatch();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Sends additional commands to a location provider.
* Can be used to support provider specific extensions to the Location Manager API
*
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 4ab894f..4a72c1f 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.app.ActivityManager;
+import android.annotation.NonNull;
import android.content.pm.PackageManagerInternal;
import com.android.internal.content.PackageMonitor;
import com.android.internal.location.ProviderProperties;
@@ -62,6 +63,7 @@
import android.location.Criteria;
import android.location.GeocoderParams;
import android.location.Geofence;
+import android.location.IBatchedLocationCallback;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssStatusListener;
import android.location.IGnssStatusProvider;
@@ -101,6 +103,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Set;
/**
@@ -229,6 +232,11 @@
private GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
+ private GnssLocationProvider.GnssBatchingProvider mGnssBatchingProvider;
+ private IBatchedLocationCallback mGnssBatchingCallback;
+ private LinkedCallback mGnssBatchingDeathCallback;
+ private boolean mGnssBatchingInProgress = false;
+
public LocationManagerService(Context context) {
super();
mContext = context;
@@ -545,6 +553,7 @@
GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext, this,
mLocationHandler.getLooper());
mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
+ mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
addProviderLocked(gnssProvider);
@@ -1073,13 +1082,197 @@
*/
@Override
public int getGnssYearOfHardware() {
- if (mGnssNavigationMessageProvider != null) {
+ if (mGnssSystemInfoProvider != null) {
return mGnssSystemInfoProvider.getGnssYearOfHardware();
} else {
return 0;
}
}
+ /**
+ * Runs some checks for GNSS (FINE) level permissions, used by several methods which directly
+ * (try to) access GNSS information at this layer.
+ */
+ private boolean hasGnssPermissions(String packageName) {
+ int allowedResolutionLevel = getCallerAllowedResolutionLevel();
+ checkResolutionLevelIsSufficientForProviderUse(
+ allowedResolutionLevel,
+ LocationManager.GPS_PROVIDER);
+
+ int pid = Binder.getCallingPid();
+ int uid = Binder.getCallingUid();
+ long identity = Binder.clearCallingIdentity();
+ boolean hasLocationAccess;
+ try {
+ hasLocationAccess = checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+
+ return hasLocationAccess;
+ }
+
+ /**
+ * Returns the GNSS batching size, if available.
+ */
+ @Override
+ public int getGnssBatchSize(String packageName) {
+ mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware batching");
+
+ if (hasGnssPermissions(packageName) && mGnssBatchingProvider != null) {
+ return mGnssBatchingProvider.getSize();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Adds a callback for GNSS Batching events, if permissions allow, which are transported
+ * to potentially multiple listeners by the BatchedLocationCallbackTransport above this.
+ */
+ @Override
+ public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
+ mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware batching");
+
+ if (!hasGnssPermissions(packageName) || mGnssBatchingProvider == null) {
+ return false;
+ }
+
+ mGnssBatchingCallback = callback;
+ mGnssBatchingDeathCallback = new LinkedCallback(callback);
+ try {
+ callback.asBinder().linkToDeath(mGnssBatchingDeathCallback, 0 /* flags */);
+ } catch (RemoteException e) {
+ // if the remote process registering the listener is already dead, just swallow the
+ // exception and return
+ Log.e(TAG, "Remote listener already died.", e);
+ return false;
+ }
+
+ return true;
+ }
+
+ private class LinkedCallback implements IBinder.DeathRecipient {
+ private final IBatchedLocationCallback mCallback;
+
+ public LinkedCallback(@NonNull IBatchedLocationCallback callback) {
+ mCallback = callback;
+ }
+
+ @NonNull
+ public IBatchedLocationCallback getUnderlyingListener() {
+ return mCallback;
+ }
+
+ @Override
+ public void binderDied() {
+ Log.d(TAG, "Remote Batching Callback died: " + mCallback);
+ stopGnssBatch();
+ removeGnssBatchingCallback();
+ }
+ }
+
+ /**
+ * Removes callback for GNSS batching
+ */
+ @Override
+ public void removeGnssBatchingCallback() {
+ try {
+ mGnssBatchingCallback.asBinder().unlinkToDeath(mGnssBatchingDeathCallback,
+ 0 /* flags */);
+ } catch (NoSuchElementException e) {
+ // if the death callback isn't connected (it should be...), log error, swallow the
+ // exception and return
+ Log.e(TAG, "Couldn't unlink death callback.", e);
+ }
+ mGnssBatchingCallback = null;
+ mGnssBatchingDeathCallback = null;
+ }
+
+
+ /**
+ * Starts GNSS batching, if available.
+ */
+ @Override
+ public boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName) {
+ mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware batching");
+
+ if (!hasGnssPermissions(packageName) || mGnssBatchingProvider == null) {
+ return false;
+ }
+
+ if (mGnssBatchingInProgress) {
+ // Current design does not expect multiple starts to be called repeatedly
+ Log.e(TAG, "startGnssBatch unexpectedly called w/o stopping prior batch");
+ // Try to clean up anyway, and continue
+ stopGnssBatch();
+ }
+
+ mGnssBatchingInProgress = true;
+ return mGnssBatchingProvider.start(periodNanos, wakeOnFifoFull);
+ }
+
+ /**
+ * Flushes a GNSS batch in progress
+ */
+ @Override
+ public void flushGnssBatch(String packageName) {
+ mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware batching");
+
+ if (!hasGnssPermissions(packageName)) {
+ Log.e(TAG, "flushGnssBatch called without GNSS permissions");
+ return;
+ }
+
+ if (!mGnssBatchingInProgress) {
+ Log.w(TAG, "flushGnssBatch called with no batch in progress");
+ }
+
+ if (mGnssBatchingProvider != null) {
+ mGnssBatchingProvider.flush();
+ }
+ }
+
+ /**
+ * Stops GNSS batching
+ */
+ @Override
+ public boolean stopGnssBatch() {
+ mContext.enforceCallingPermission(android.Manifest.permission.LOCATION_HARDWARE,
+ "Location Hardware permission not granted to access hardware batching");
+
+ if (mGnssBatchingProvider != null) {
+ mGnssBatchingInProgress = false;
+ return mGnssBatchingProvider.stop();
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public void reportLocationBatch(List<Location> locations) {
+ checkCallerIsProvider();
+
+ // Currently used only for GNSS locations - update permissions check if changed
+ if (isAllowedByCurrentUserSettingsLocked(LocationManager.GPS_PROVIDER)) {
+ if (mGnssBatchingCallback == null) {
+ Slog.e(TAG, "reportLocationBatch() called without active Callback");
+ return;
+ }
+ try {
+ mGnssBatchingCallback.onLocationBatch(locations);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "mGnssBatchingCallback.onLocationBatch failed", e);
+ }
+ } else {
+ Slog.w(TAG, "reportLocationBatch() called without user permission, locations blocked");
+ }
+ }
+
private void addProviderLocked(LocationProviderInterface provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
@@ -2000,22 +2193,7 @@
@Override
public boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName) {
- int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
- LocationManager.GPS_PROVIDER);
-
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- final long ident = Binder.clearCallingIdentity();
- try {
- if (!checkLocationAccess(pid, uid, packageName, allowedResolutionLevel)) {
- return false;
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
-
- if (mGnssStatusProvider == null) {
+ if (!hasGnssPermissions(packageName) || mGnssStatusProvider == null) {
return false;
}
@@ -2043,22 +2221,7 @@
public boolean addGnssMeasurementsListener(
IGnssMeasurementsListener listener,
String packageName) {
- int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkResolutionLevelIsSufficientForProviderUse(
- allowedResolutionLevel,
- LocationManager.GPS_PROVIDER);
-
- int pid = Binder.getCallingPid();
- int uid = Binder.getCallingUid();
- long identity = Binder.clearCallingIdentity();
- boolean hasLocationAccess;
- try {
- hasLocationAccess = checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- if (!hasLocationAccess || mGnssMeasurementsProvider == null) {
+ if (!hasGnssPermissions(packageName) || mGnssMeasurementsProvider == null) {
return false;
}
return mGnssMeasurementsProvider.addListener(listener);
@@ -2075,22 +2238,7 @@
public boolean addGnssNavigationMessageListener(
IGnssNavigationMessageListener listener,
String packageName) {
- int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkResolutionLevelIsSufficientForProviderUse(
- allowedResolutionLevel,
- LocationManager.GPS_PROVIDER);
-
- int pid = Binder.getCallingPid();
- int uid = Binder.getCallingUid();
- long identity = Binder.clearCallingIdentity();
- boolean hasLocationAccess;
- try {
- hasLocationAccess = checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- if (!hasLocationAccess || mGnssNavigationMessageProvider == null) {
+ if (!hasGnssPermissions(packageName) || mGnssNavigationMessageProvider == null) {
return false;
}
return mGnssNavigationMessageProvider.addListener(listener);
@@ -2866,6 +3014,9 @@
pw.println(":");
provider.dump(fd, pw, args);
}
+ if (mGnssBatchingInProgress) {
+ pw.println(" GNSS batching in progress");
+ }
}
}
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index eb8f8fc..17b005d 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -72,7 +72,6 @@
import android.provider.Settings;
import android.provider.Telephony.Carriers;
import android.provider.Telephony.Sms.Intents;
-import android.telephony.SmsMessage;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
@@ -82,17 +81,17 @@
import android.util.Log;
import android.util.NtpTrustedTime;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.io.StringReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
+import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Map;
@@ -411,7 +410,6 @@
private WorkSource mClientSource = new WorkSource();
private GeofenceHardwareImpl mGeofenceHardwareImpl;
-
private int mYearOfHardware = 0;
// Set lower than the current ITAR limit of 600m/s to allow this to trigger even if GPS HAL
@@ -1125,6 +1123,7 @@
mGnssMeasurementsProvider.onGpsEnabledChanged();
mGnssNavigationMessageProvider.onGpsEnabledChanged();
+ enableBatching();
} else {
synchronized (mLock) {
mEnabled = false;
@@ -1156,6 +1155,7 @@
mAlarmManager.cancel(mWakeupIntent);
mAlarmManager.cancel(mTimeoutIntent);
+ disableBatching();
// do this before releasing wakelock
native_cleanup();
@@ -1791,6 +1791,84 @@
};
}
+ public interface GnssBatchingProvider {
+ /**
+ * Returns the GNSS batching size
+ */
+ int getSize();
+ /**
+ * Starts the hardware batching operation
+ */
+ boolean start(long periodNanos, boolean wakeOnFifoFull);
+ /**
+ * Forces a flush of existing locations from the hardware batching
+ */
+ void flush();
+ /**
+ * Stops the batching operation
+ */
+ boolean stop();
+ }
+
+ /**
+ * @hide
+ */
+ public GnssBatchingProvider getGnssBatchingProvider() {
+ return new GnssBatchingProvider() {
+ @Override
+ public int getSize() {
+ return native_get_batch_size();
+ }
+ @Override
+ public boolean start(long periodNanos, boolean wakeOnFifoFull) {
+ if (periodNanos <= 0) {
+ Log.e(TAG, "Invalid periodNanos " + periodNanos +
+ "in batching request, not started");
+ return false;
+ }
+ return native_start_batch(periodNanos, wakeOnFifoFull);
+ }
+ @Override
+ public void flush() {
+ native_flush_batch();
+ }
+ @Override
+ public boolean stop() {
+ return native_stop_batch();
+ }
+ };
+ }
+
+ /**
+ * Initialize Batching if enabled
+ */
+ private void enableBatching() {
+ if (!native_init_batching()) {
+ Log.e(TAG, "Failed to initialize GNSS batching");
+ };
+ }
+
+ /**
+ * Disable batching
+ */
+ private void disableBatching() {
+ native_stop_batch();
+ native_cleanup_batching();
+ }
+
+ /**
+ * called from native code - GNSS location batch callback
+ */
+ private void reportLocationBatch(Location[] locationArray) {
+ List<Location> locations = new ArrayList<>(Arrays.asList(locationArray));
+ if(DEBUG) { Log.d(TAG, "Location batch of size " + locationArray.length + "reported"); }
+ try {
+ mILocationManager.reportLocationBatch(locations);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocationBatch");
+ }
+ }
+
/**
* called from native code to request XTRA data
*/
@@ -2117,7 +2195,10 @@
// note that this assumes the message will not be removed from the queue before
// it is handled (otherwise the wake lock would be leaked).
mWakeLock.acquire();
- Log.i(TAG, "WakeLock acquired by sendMessage(" + message + ", " + arg + ", " + obj + ")");
+ if (Log.isLoggable(TAG, Log.INFO)) {
+ Log.i(TAG, "WakeLock acquired by sendMessage(" + messageIdAsString(message) + ", " + arg
+ + ", " + obj + ")");
+ }
mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
}
@@ -2175,8 +2256,10 @@
if (msg.arg2 == 1) {
// wakelock was taken for this message, release it
mWakeLock.release();
- Log.i(TAG, "WakeLock released by handleMessage(" + message + ", " + msg.arg1 + ", "
- + msg.obj + ")");
+ if (Log.isLoggable(TAG, Log.INFO)) {
+ Log.i(TAG, "WakeLock released by handleMessage(" + messageIdAsString(message)
+ + ", " + msg.arg1 + ", " + msg.obj + ")");
+ }
}
}
@@ -2424,6 +2507,40 @@
}
}
+ /**
+ * @return A string representing the given message ID.
+ */
+ private String messageIdAsString(int message) {
+ switch (message) {
+ case ENABLE:
+ return "ENABLE";
+ case SET_REQUEST:
+ return "SET_REQUEST";
+ case UPDATE_NETWORK_STATE:
+ return "UPDATE_NETWORK_STATE";
+ case REQUEST_SUPL_CONNECTION:
+ return "REQUEST_SUPL_CONNECTION";
+ case RELEASE_SUPL_CONNECTION:
+ return "RELEASE_SUPL_CONNECTION";
+ case INJECT_NTP_TIME:
+ return "INJECT_NTP_TIME";
+ case DOWNLOAD_XTRA_DATA:
+ return "DOWNLOAD_XTRA_DATA";
+ case INJECT_NTP_TIME_FINISHED:
+ return "INJECT_NTP_TIME_FINISHED";
+ case DOWNLOAD_XTRA_DATA_FINISHED:
+ return "DOWNLOAD_XTRA_DATA_FINISHED";
+ case UPDATE_LOCATION:
+ return "UPDATE_LOCATION";
+ case SUBSCRIPTION_OR_SIM_CHANGED:
+ return "SUBSCRIPTION_OR_SIM_CHANGED";
+ case INITIALIZE_HANDLER:
+ return "INITIALIZE_HANDLER";
+ default:
+ return "<Unknown>";
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
StringBuilder s = new StringBuilder();
@@ -2441,7 +2558,9 @@
if (hasCapability(GPS_CAPABILITY_NAV_MESSAGES)) s.append("NAV_MESSAGES ");
s.append(")\n");
- s.append(native_get_internal_state());
+ s.append(" internal state: ").append(native_get_internal_state());
+ s.append("\n");
+
pw.append(s);
}
@@ -2562,4 +2681,12 @@
private static native boolean native_set_gps_lock(int gpsLock);
private static native boolean native_set_emergency_supl_pdn(int emergencySuplPdn);
+ // GNSS Batching
+ private static native int native_get_batch_size();
+ private static native boolean native_start_batch(long periodNanos, boolean wakeOnFifoFull);
+ private static native void native_flush_batch();
+ private static native boolean native_stop_batch();
+ private static native boolean native_init_batching();
+ private static native void native_cleanup_batching();
+
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 01a1efc..1578562 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -58,6 +58,7 @@
static jmethodID method_reportGeofenceResumeStatus;
static jmethodID method_reportMeasurementData;
static jmethodID method_reportNavigationMessages;
+static jmethodID method_reportLocationBatch;
/*
* Save a pointer to JavaVm to attach/detach threads executing
@@ -80,6 +81,8 @@
using android::hardware::gnss::V1_0::IAGnssRil;
using android::hardware::gnss::V1_0::IAGnssRilCallback;
using android::hardware::gnss::V1_0::IGnss;
+using android::hardware::gnss::V1_0::IGnssBatching;
+using android::hardware::gnss::V1_0::IGnssBatchingCallback;
using android::hardware::gnss::V1_0::IGnssCallback;
using android::hardware::gnss::V1_0::IGnssConfiguration;
using android::hardware::gnss::V1_0::IGnssDebug;
@@ -100,6 +103,7 @@
sp<IAGnssRil> agnssRilIface = nullptr;
sp<IGnssGeofencing> gnssGeofencingIface = nullptr;
sp<IAGnss> agnssIface = nullptr;
+sp<IGnssBatching> gnssBatchingIface = nullptr;
sp<IGnssDebug> gnssDebugIface = nullptr;
sp<IGnssConfiguration> gnssConfigurationIface = nullptr;
sp<IGnssNi> gnssNiIface = nullptr;
@@ -139,6 +143,7 @@
class JavaObject {
public:
JavaObject(JNIEnv* env, const char* class_name);
+ JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1);
virtual ~JavaObject();
template<class T>
@@ -159,6 +164,12 @@
object_ = env_->NewObject(clazz_, ctor);
}
+JavaObject::JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1) : env_(env) {
+ clazz_ = env_->FindClass(class_name);
+ jmethodID ctor = env->GetMethodID(clazz_, "<init>", "(Ljava/lang/String;)V");
+ object_ = env_->NewObject(clazz_, ctor, env->NewStringUTF(sz_arg_1));
+}
+
JavaObject::~JavaObject() {
env_->DeleteLocalRef(clazz_);
}
@@ -591,6 +602,7 @@
env->CallVoidMethod(mCallbacksObj,
method_reportNavigationMessages,
navigationMessage);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
env->DeleteLocalRef(navigationMessage);
return Void();
}
@@ -925,6 +937,81 @@
return Void();
}
+/*
+ * GnssBatchingCallback interface implements the callback methods
+ * required by the IGnssBatching interface.
+ */
+struct GnssBatchingCallback : public IGnssBatchingCallback {
+ /*
+ * Methods from ::android::hardware::gps::V1_0::IGnssBatchingCallback
+ * follow.
+ */
+ Return<void> gnssLocationBatchCb(
+ const ::android::hardware::hidl_vec<hardware::gnss::V1_0::GnssLocation> & locations)
+ override;
+ private:
+ jobject translateLocation(
+ JNIEnv* env, const hardware::gnss::V1_0::GnssLocation* location);
+};
+
+Return<void> GnssBatchingCallback::gnssLocationBatchCb(
+ const ::android::hardware::hidl_vec<hardware::gnss::V1_0::GnssLocation> & locations) {
+ JNIEnv* env = getJniEnv();
+
+ jobjectArray jLocations = env->NewObjectArray(locations.size(),
+ env->FindClass("android/location/Location"), nullptr);
+
+ for (uint16_t i = 0; i < locations.size(); ++i) {
+ jobject jLocation = translateLocation(env, &locations[i]);
+ env->SetObjectArrayElement(jLocations, i, jLocation);
+ env->DeleteLocalRef(jLocation);
+ }
+
+ env->CallVoidMethod(mCallbacksObj, method_reportLocationBatch, jLocations);
+ checkAndClearExceptionFromCallback(env, __FUNCTION__);
+
+ env->DeleteLocalRef(jLocations);
+
+ return Void();
+}
+
+// TODO: Use this common code to translate location for Geofencing and regular Location
+jobject GnssBatchingCallback::translateLocation(
+ JNIEnv* env, const hardware::gnss::V1_0::GnssLocation* location) {
+ JavaObject object(env, "android/location/Location", "gps");
+
+ uint16_t flags = static_cast<uint32_t>(location->gnssLocationFlags);
+ if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_LAT_LONG) {
+ SET(Latitude, location->latitudeDegrees);
+ SET(Longitude, location->longitudeDegrees);
+ }
+ if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_ALTITUDE) {
+ SET(Altitude, location->altitudeMeters);
+ }
+ if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED) {
+ SET(Speed, location->speedMetersPerSec);
+ }
+ if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING) {
+ SET(Bearing, location->bearingDegrees);
+ }
+ if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_HORIZONTAL_ACCURACY) {
+ SET(Accuracy, location->horizontalAccuracyMeters);
+ }
+ if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_VERTICAL_ACCURACY) {
+ SET(VerticalAccuracyMeters, location->verticalAccuracyMeters);
+ }
+ if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_SPEED_ACCURACY) {
+ SET(SpeedAccuracyMetersPerSecond, location->speedAccuracyMetersPerSecond);
+ }
+ if (flags & hardware::gnss::V1_0::GnssLocationFlags::HAS_BEARING_ACCURACY) {
+ SET(BearingAccuracyDegrees, location->bearingAccuracyDegrees);
+ }
+ SET(Time, location->timestamp);
+
+ return object.get();
+}
+
+
static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFFFFJ)V");
method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
@@ -959,6 +1046,10 @@
clazz,
"reportNavigationMessage",
"(Landroid/location/GnssNavigationMessage;)V");
+ method_reportLocationBatch = env->GetMethodID(
+ clazz,
+ "reportLocationBatch",
+ "([Landroid/location/Location;)V");
/*
* Save a pointer to JVM.
@@ -1033,6 +1124,13 @@
} else {
gnssGeofencingIface = gnssGeofencing;
}
+
+ auto gnssBatching = gnssHal->getExtensionGnssBatching();
+ if (!gnssBatching.isOk()) {
+ ALOGD("Unable to get a handle to gnssBatching");
+ } else {
+ gnssBatchingIface = gnssBatching;
+ }
} else {
ALOGE("Unable to get GPS service\n");
}
@@ -1704,6 +1802,67 @@
}
}
+static jint android_location_GnssLocationProvider_get_batch_size(JNIEnv*, jclass) {
+ if (gnssBatchingIface == nullptr) {
+ return 0; // batching not supported, size = 0
+ }
+ auto result = gnssBatchingIface->getBatchSize();
+ if (result.isOk()) {
+ return static_cast<jint>(result);
+ } else {
+ return 0; // failure in binder, don't support batching
+ }
+}
+
+static jboolean android_location_GnssLocationProvider_init_batching(JNIEnv*, jclass) {
+ if (gnssBatchingIface == nullptr) {
+ return JNI_FALSE; // batching not supported
+ }
+ sp<IGnssBatchingCallback> gnssBatchingCbIface = new GnssBatchingCallback();
+
+ return static_cast<jboolean>(gnssBatchingIface->init(gnssBatchingCbIface));
+}
+
+static void android_location_GnssLocationProvider_cleanup_batching(JNIEnv*, jclass) {
+ if (gnssBatchingIface == nullptr) {
+ return; // batching not supported
+ }
+ gnssBatchingIface->cleanup();
+}
+
+static jboolean android_location_GnssLocationProvider_start_batch(JNIEnv*, jclass,
+ jlong periodNanos, jboolean wakeOnFifoFull) {
+ if (gnssBatchingIface == nullptr) {
+ return JNI_FALSE; // batching not supported
+ }
+
+ IGnssBatching::Options options;
+ options.periodNanos = periodNanos;
+ if (wakeOnFifoFull) {
+ options.flags = static_cast<uint8_t>(IGnssBatching::Flag::WAKEUP_ON_FIFO_FULL);
+ } else {
+ options.flags = 0;
+ }
+
+ return static_cast<jboolean>(gnssBatchingIface->start(options));
+}
+
+static void android_location_GnssLocationProvider_flush_batch(JNIEnv*, jclass) {
+ if (gnssBatchingIface == nullptr) {
+ return; // batching not supported
+ }
+
+ gnssBatchingIface->flush();
+}
+
+static jboolean android_location_GnssLocationProvider_stop_batch(JNIEnv*, jclass) {
+ if (gnssBatchingIface == nullptr) {
+ return JNI_FALSE; // batching not supported
+ }
+
+ return gnssBatchingIface->stop();
+}
+
static const JNINativeMethod sMethods[] = {
/* name, signature, funcPtr */
{"class_init_native", "()V", reinterpret_cast<void *>(
@@ -1829,6 +1988,27 @@
{"native_set_emergency_supl_pdn",
"(I)Z",
reinterpret_cast<void *>(android_location_GnssLocationProvider_set_emergency_supl_pdn)},
+ {"native_get_batch_size",
+ "()I",
+ reinterpret_cast<void *>(android_location_GnssLocationProvider_get_batch_size)},
+ {"native_init_batching",
+ "()Z",
+ reinterpret_cast<void *>(android_location_GnssLocationProvider_init_batching)},
+ {"native_start_batch",
+ "(JZ)Z",
+ reinterpret_cast<void *>(android_location_GnssLocationProvider_start_batch)},
+ {"native_flush_batch",
+ "()V",
+ reinterpret_cast<void *>(android_location_GnssLocationProvider_flush_batch)},
+ {"native_stop_batch",
+ "()Z",
+ reinterpret_cast<void *>(android_location_GnssLocationProvider_stop_batch)},
+ {"native_init_batching",
+ "()Z",
+ reinterpret_cast<void *>(android_location_GnssLocationProvider_init_batching)},
+ {"native_cleanup_batching",
+ "()V",
+ reinterpret_cast<void *>(android_location_GnssLocationProvider_cleanup_batching)},
};
int register_android_server_location_GnssLocationProvider(JNIEnv* env) {