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/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");
+ }
}
}
}