Add fullTracking to registerGnssMeasurementsCallback (implementation)
- Currently enableFullTracking has a toggle in Developer Option.
This change adds a system API for the same functionality.
- If at least one of <enableFullTracking in Developer Option> and
<enableFullTracking from the system API> is true, full tracking is
enabled when registering a GnssMeasurement callback.
- For multiple GnssMeasurement callbacks, full tracking is enabled
if *at least* one callback has fullTracking enabled. Removing a callback
might trigger restarting the measurement collection if the aggregated
requirement has changed.
Bug: 147387008
Test: atest GnssMeasurementsProviderTest
atest GnssManagerServiceTest
atest GnssMeasurementRegistrationTest
Change-Id: Iedf03edd7053aa0089cfea0d5c0357322f68d9c4
This commit is contained in:
@@ -27,6 +27,7 @@ import android.util.ArrayMap;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -35,26 +36,34 @@ import java.util.function.Consumer;
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
abstract class AbstractListenerManager<T> {
|
||||
abstract class AbstractListenerManager<TRequest, TListener> {
|
||||
|
||||
private static class Registration<T> {
|
||||
private static class Registration<TRequest, TListener> {
|
||||
private final Executor mExecutor;
|
||||
@Nullable private volatile T mListener;
|
||||
@Nullable private TRequest mRequest;
|
||||
@Nullable private volatile TListener mListener;
|
||||
|
||||
private Registration(Executor executor, T listener) {
|
||||
private Registration(@Nullable TRequest request, Executor executor, TListener listener) {
|
||||
Preconditions.checkArgument(listener != null, "invalid null listener/callback");
|
||||
Preconditions.checkArgument(executor != null, "invalid null executor");
|
||||
mExecutor = executor;
|
||||
mListener = listener;
|
||||
mRequest = request;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TRequest getRequest() {
|
||||
return mRequest;
|
||||
}
|
||||
|
||||
private void unregister() {
|
||||
mRequest = null;
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
private void execute(Consumer<T> operation) {
|
||||
private void execute(Consumer<TListener> operation) {
|
||||
mExecutor.execute(() -> {
|
||||
T listener = mListener;
|
||||
TListener listener = mListener;
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
@@ -71,71 +80,135 @@ abstract class AbstractListenerManager<T> {
|
||||
}
|
||||
|
||||
@GuardedBy("mListeners")
|
||||
private final ArrayMap<Object, Registration<T>> mListeners = new ArrayMap<>();
|
||||
private final ArrayMap<Object, Registration<TRequest, TListener>> mListeners =
|
||||
new ArrayMap<>();
|
||||
|
||||
public boolean addListener(@NonNull T listener, @NonNull Handler handler)
|
||||
@GuardedBy("mListeners")
|
||||
@Nullable
|
||||
private TRequest mMergedRequest;
|
||||
|
||||
public boolean addListener(@NonNull TListener listener, @NonNull Handler handler)
|
||||
throws RemoteException {
|
||||
return addInternal(listener, handler);
|
||||
return addInternal(/* request= */ null, listener, handler);
|
||||
}
|
||||
|
||||
public boolean addListener(@NonNull T listener, @NonNull Executor executor)
|
||||
public boolean addListener(@NonNull TListener listener, @NonNull Executor executor)
|
||||
throws RemoteException {
|
||||
return addInternal(listener, executor);
|
||||
return addInternal(/* request= */ null, listener, executor);
|
||||
}
|
||||
|
||||
protected final boolean addInternal(@NonNull Object listener, @NonNull Handler handler)
|
||||
throws RemoteException {
|
||||
return addInternal(listener, new HandlerExecutor(handler));
|
||||
public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
|
||||
@NonNull Handler handler) throws RemoteException {
|
||||
return addInternal(request, listener, handler);
|
||||
}
|
||||
|
||||
protected final boolean addInternal(@NonNull Object listener, @NonNull Executor executor)
|
||||
public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
|
||||
@NonNull Executor executor) throws RemoteException {
|
||||
return addInternal(request, listener, executor);
|
||||
}
|
||||
|
||||
protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
|
||||
@NonNull Handler handler) throws RemoteException {
|
||||
return addInternal(request, listener, new HandlerExecutor(handler));
|
||||
}
|
||||
|
||||
protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
|
||||
@NonNull Executor executor)
|
||||
throws RemoteException {
|
||||
Preconditions.checkArgument(listener != null, "invalid null listener/callback");
|
||||
return addInternal(listener, new Registration<>(executor, convertKey(listener)));
|
||||
return addInternal(listener, new Registration<>(request, executor, convertKey(listener)));
|
||||
}
|
||||
|
||||
private boolean addInternal(Object key, Registration<T> registration) throws RemoteException {
|
||||
private boolean addInternal(Object key, Registration<TRequest, TListener> registration)
|
||||
throws RemoteException {
|
||||
Preconditions.checkNotNull(registration);
|
||||
|
||||
synchronized (mListeners) {
|
||||
if (mListeners.isEmpty() && !registerService()) {
|
||||
return false;
|
||||
}
|
||||
Registration<T> oldRegistration = mListeners.put(key, registration);
|
||||
boolean initialRequest = mListeners.isEmpty();
|
||||
|
||||
Registration<TRequest, TListener> oldRegistration = mListeners.put(key, registration);
|
||||
if (oldRegistration != null) {
|
||||
oldRegistration.unregister();
|
||||
}
|
||||
TRequest merged = mergeRequests();
|
||||
|
||||
if (initialRequest || !Objects.equals(merged, mMergedRequest)) {
|
||||
mMergedRequest = merged;
|
||||
if (!initialRequest) {
|
||||
unregisterService();
|
||||
}
|
||||
registerService(mMergedRequest);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void removeListener(Object listener) throws RemoteException {
|
||||
synchronized (mListeners) {
|
||||
Registration<T> oldRegistration = mListeners.remove(listener);
|
||||
Registration<TRequest, TListener> oldRegistration = mListeners.remove(listener);
|
||||
if (oldRegistration == null) {
|
||||
return;
|
||||
}
|
||||
oldRegistration.unregister();
|
||||
|
||||
if (mListeners.isEmpty()) {
|
||||
boolean lastRequest = mListeners.isEmpty();
|
||||
TRequest merged = lastRequest ? null : mergeRequests();
|
||||
boolean newRequest = !lastRequest && !Objects.equals(merged, mMergedRequest);
|
||||
|
||||
if (lastRequest || newRequest) {
|
||||
unregisterService();
|
||||
mMergedRequest = merged;
|
||||
if (newRequest) {
|
||||
registerService(mMergedRequest);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected T convertKey(@NonNull Object listener) {
|
||||
return (T) listener;
|
||||
protected TListener convertKey(@NonNull Object listener) {
|
||||
return (TListener) listener;
|
||||
}
|
||||
|
||||
protected abstract boolean registerService() throws RemoteException;
|
||||
protected abstract boolean registerService(TRequest request) throws RemoteException;
|
||||
protected abstract void unregisterService() throws RemoteException;
|
||||
|
||||
protected void execute(Consumer<T> operation) {
|
||||
@Nullable
|
||||
protected TRequest merge(@NonNull TRequest[] requests) {
|
||||
for (TRequest request : requests) {
|
||||
Preconditions.checkArgument(request == null,
|
||||
"merge() has to be overridden for non-null requests.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void execute(Consumer<TListener> operation) {
|
||||
synchronized (mListeners) {
|
||||
for (Registration<T> registration : mListeners.values()) {
|
||||
for (Registration<TRequest, TListener> registration : mListeners.values()) {
|
||||
registration.execute(operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mListeners")
|
||||
@SuppressWarnings("unchecked")
|
||||
@Nullable
|
||||
private TRequest mergeRequests() {
|
||||
Preconditions.checkState(Thread.holdsLock(mListeners));
|
||||
|
||||
if (mListeners.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mListeners.size() == 1) {
|
||||
return mListeners.valueAt(0).getRequest();
|
||||
}
|
||||
|
||||
TRequest[] requests = (TRequest[]) new Object[mListeners.size()];
|
||||
for (int index = 0; index < mListeners.size(); index++) {
|
||||
requests[index] = mListeners.valueAt(index).getRequest();
|
||||
}
|
||||
return merge(requests);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.location.Criteria;
|
||||
import android.location.GeocoderParams;
|
||||
import android.location.Geofence;
|
||||
import android.location.GnssMeasurementCorrections;
|
||||
import android.location.GnssRequest;
|
||||
import android.location.IBatchedLocationCallback;
|
||||
import android.location.IGnssMeasurementsListener;
|
||||
import android.location.IGnssStatusListener;
|
||||
@@ -69,8 +70,10 @@ interface ILocationManager
|
||||
double upperRightLatitude, double upperRightLongitude, int maxResults,
|
||||
in GeocoderParams params, out List<Address> addrs);
|
||||
|
||||
boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener,
|
||||
String packageName, String featureId, String listenerIdentifier);
|
||||
boolean addGnssMeasurementsListener(in GnssRequest request,
|
||||
in IGnssMeasurementsListener listener,
|
||||
String packageName, String featureId,
|
||||
String listenerIdentifier);
|
||||
void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
|
||||
in String packageName);
|
||||
long getGnssCapabilities(in String packageName);
|
||||
|
||||
@@ -2199,7 +2199,7 @@ public class LocationManager {
|
||||
* Registers a GNSS Measurement callback.
|
||||
*
|
||||
* @param request extra parameters to pass to GNSS measurement provider. For example, if {@link
|
||||
* GnssRequest#isFullTrackingEnabled()} is true, GNSS chipset switches off duty
|
||||
* GnssRequest#isFullTracking()} is true, GNSS chipset switches off duty
|
||||
* cycling.
|
||||
* @param executor the executor that the callback runs on.
|
||||
* @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
|
||||
@@ -2216,7 +2216,12 @@ public class LocationManager {
|
||||
@NonNull GnssRequest request,
|
||||
@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull GnssMeasurementsEvent.Callback callback) {
|
||||
throw new RuntimeException();
|
||||
Preconditions.checkArgument(request != null, "invalid null request");
|
||||
try {
|
||||
return mGnssMeasurementsListenerManager.addListener(request, callback, executor);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2763,8 +2768,7 @@ public class LocationManager {
|
||||
}
|
||||
|
||||
private class GnssStatusListenerManager extends
|
||||
AbstractListenerManager<GnssStatus.Callback> {
|
||||
|
||||
AbstractListenerManager<Void, GnssStatus.Callback> {
|
||||
@Nullable
|
||||
private IGnssStatusListener mListenerTransport;
|
||||
|
||||
@@ -2782,19 +2786,19 @@ public class LocationManager {
|
||||
|
||||
public boolean addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor)
|
||||
throws RemoteException {
|
||||
return addInternal(listener, executor);
|
||||
return addInternal(null, listener, executor);
|
||||
}
|
||||
|
||||
public boolean addListener(@NonNull OnNmeaMessageListener listener,
|
||||
@NonNull Handler handler)
|
||||
throws RemoteException {
|
||||
return addInternal(listener, handler);
|
||||
return addInternal(null, listener, handler);
|
||||
}
|
||||
|
||||
public boolean addListener(@NonNull OnNmeaMessageListener listener,
|
||||
@NonNull Executor executor)
|
||||
throws RemoteException {
|
||||
return addInternal(listener, executor);
|
||||
return addInternal(null, listener, executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -2833,7 +2837,7 @@ public class LocationManager {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean registerService() throws RemoteException {
|
||||
protected boolean registerService(Void ignored) throws RemoteException {
|
||||
Preconditions.checkState(mListenerTransport == null);
|
||||
|
||||
GnssStatusListener transport = new GnssStatusListener();
|
||||
@@ -2893,17 +2897,17 @@ public class LocationManager {
|
||||
}
|
||||
|
||||
private class GnssMeasurementsListenerManager extends
|
||||
AbstractListenerManager<GnssMeasurementsEvent.Callback> {
|
||||
AbstractListenerManager<GnssRequest, GnssMeasurementsEvent.Callback> {
|
||||
|
||||
@Nullable
|
||||
private IGnssMeasurementsListener mListenerTransport;
|
||||
|
||||
@Override
|
||||
protected boolean registerService() throws RemoteException {
|
||||
protected boolean registerService(GnssRequest request) throws RemoteException {
|
||||
Preconditions.checkState(mListenerTransport == null);
|
||||
|
||||
GnssMeasurementsListener transport = new GnssMeasurementsListener();
|
||||
if (mService.addGnssMeasurementsListener(transport, mContext.getPackageName(),
|
||||
if (mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(),
|
||||
mContext.getFeatureId(), "gnss measurement callback")) {
|
||||
mListenerTransport = transport;
|
||||
return true;
|
||||
@@ -2920,6 +2924,18 @@ public class LocationManager {
|
||||
mListenerTransport = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected GnssRequest merge(@NonNull GnssRequest[] requests) {
|
||||
Preconditions.checkArgument(requests.length > 0);
|
||||
for (GnssRequest request : requests) {
|
||||
if (request.isFullTracking()) {
|
||||
return request;
|
||||
}
|
||||
}
|
||||
return requests[0];
|
||||
}
|
||||
|
||||
private class GnssMeasurementsListener extends IGnssMeasurementsListener.Stub {
|
||||
@Override
|
||||
public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
|
||||
@@ -2934,13 +2950,13 @@ public class LocationManager {
|
||||
}
|
||||
|
||||
private class GnssNavigationMessageListenerManager extends
|
||||
AbstractListenerManager<GnssNavigationMessage.Callback> {
|
||||
AbstractListenerManager<Void, GnssNavigationMessage.Callback> {
|
||||
|
||||
@Nullable
|
||||
private IGnssNavigationMessageListener mListenerTransport;
|
||||
|
||||
@Override
|
||||
protected boolean registerService() throws RemoteException {
|
||||
protected boolean registerService(Void ignored) throws RemoteException {
|
||||
Preconditions.checkState(mListenerTransport == null);
|
||||
|
||||
GnssNavigationMessageListener transport = new GnssNavigationMessageListener();
|
||||
@@ -2975,13 +2991,13 @@ public class LocationManager {
|
||||
}
|
||||
|
||||
private class BatchedLocationCallbackManager extends
|
||||
AbstractListenerManager<BatchedLocationCallback> {
|
||||
AbstractListenerManager<Void, BatchedLocationCallback> {
|
||||
|
||||
@Nullable
|
||||
private IBatchedLocationCallback mListenerTransport;
|
||||
|
||||
@Override
|
||||
protected boolean registerService() throws RemoteException {
|
||||
protected boolean registerService(Void ignored) throws RemoteException {
|
||||
Preconditions.checkState(mListenerTransport == null);
|
||||
|
||||
BatchedLocationCallback transport = new BatchedLocationCallback();
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.location.Criteria;
|
||||
import android.location.GeocoderParams;
|
||||
import android.location.Geofence;
|
||||
import android.location.GnssMeasurementCorrections;
|
||||
import android.location.GnssRequest;
|
||||
import android.location.IBatchedLocationCallback;
|
||||
import android.location.IGnssMeasurementsListener;
|
||||
import android.location.IGnssNavigationMessageListener;
|
||||
@@ -2287,12 +2288,14 @@ public class LocationManagerService extends ILocationManager.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addGnssMeasurementsListener(IGnssMeasurementsListener listener,
|
||||
String packageName, String featureId, String listenerIdentifier) {
|
||||
public boolean addGnssMeasurementsListener(@Nullable GnssRequest request,
|
||||
IGnssMeasurementsListener listener,
|
||||
String packageName, String featureId,
|
||||
String listenerIdentifier) {
|
||||
Objects.requireNonNull(listenerIdentifier);
|
||||
|
||||
return mGnssManagerService != null && mGnssManagerService.addGnssMeasurementsListener(
|
||||
listener, packageName, featureId, listenerIdentifier);
|
||||
request, listener, packageName, featureId, listenerIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
@@ -37,22 +38,31 @@ public class LocationManagerServiceUtils {
|
||||
/**
|
||||
* Listener that can be linked to a binder.
|
||||
* @param <TListener> listener type
|
||||
* @param <TRequest> request type
|
||||
*/
|
||||
public static class LinkedListener<TListener> extends
|
||||
public static class LinkedListener<TRequest, TListener> extends
|
||||
LinkedListenerBase {
|
||||
@Nullable protected final TRequest mRequest;
|
||||
private final TListener mListener;
|
||||
private final Consumer<TListener> mBinderDeathCallback;
|
||||
|
||||
public LinkedListener(
|
||||
@Nullable TRequest request,
|
||||
@NonNull TListener listener,
|
||||
String listenerName,
|
||||
@NonNull CallerIdentity callerIdentity,
|
||||
@NonNull Consumer<TListener> binderDeathCallback) {
|
||||
super(callerIdentity, listenerName);
|
||||
mListener = listener;
|
||||
mRequest = request;
|
||||
mBinderDeathCallback = binderDeathCallback;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public TRequest getRequest() {
|
||||
return mRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
if (D) Log.d(TAG, "Remote " + mListenerName + " died.");
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server.location;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.GnssMeasurementsEvent;
|
||||
import android.location.GnssRequest;
|
||||
import android.location.IGnssMeasurementsListener;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
@@ -33,14 +34,14 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
* @hide
|
||||
*/
|
||||
public abstract class GnssMeasurementsProvider
|
||||
extends RemoteListenerHelper<IGnssMeasurementsListener> {
|
||||
private static final String TAG = "GnssMeasurementsProvider";
|
||||
extends RemoteListenerHelper<GnssRequest, IGnssMeasurementsListener> {
|
||||
private static final String TAG = "GnssMeasProvider";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
private final GnssMeasurementProviderNative mNative;
|
||||
|
||||
private boolean mIsCollectionStarted;
|
||||
private boolean mEnableFullTracking;
|
||||
private boolean mStartedCollection;
|
||||
private boolean mStartedFullTracking;
|
||||
|
||||
protected GnssMeasurementsProvider(Context context, Handler handler) {
|
||||
this(context, handler, new GnssMeasurementProviderNative());
|
||||
@@ -57,8 +58,8 @@ public abstract class GnssMeasurementsProvider
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "resumeIfStarted");
|
||||
}
|
||||
if (mIsCollectionStarted) {
|
||||
mNative.startMeasurementCollection(mEnableFullTracking);
|
||||
if (mStartedCollection) {
|
||||
mNative.startMeasurementCollection(mStartedFullTracking);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,18 +68,35 @@ public abstract class GnssMeasurementsProvider
|
||||
return mNative.isMeasurementSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int registerWithService() {
|
||||
private boolean getMergedFullTracking() {
|
||||
int devOptions = Settings.Secure.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
|
||||
int fullTrackingToggled = Settings.Global.getInt(mContext.getContentResolver(),
|
||||
int enableFullTracking = Settings.Global.getInt(mContext.getContentResolver(),
|
||||
Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, 0);
|
||||
boolean enableFullTracking = (devOptions == 1 /* Developer Mode enabled */)
|
||||
&& (fullTrackingToggled == 1 /* Raw Measurements Full Tracking enabled */);
|
||||
boolean enableFullTrackingBySetting = (devOptions == 1 /* Developer Mode enabled */)
|
||||
&& (enableFullTracking == 1 /* Raw Measurements Full Tracking enabled */);
|
||||
if (enableFullTrackingBySetting) {
|
||||
return true;
|
||||
}
|
||||
|
||||
synchronized (mListenerMap) {
|
||||
for (IdentifiedListener identifiedListener : mListenerMap.values()) {
|
||||
GnssRequest request = identifiedListener.getRequest();
|
||||
if (request != null && request.isFullTracking()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int registerWithService() {
|
||||
boolean enableFullTracking = getMergedFullTracking();
|
||||
boolean result = mNative.startMeasurementCollection(enableFullTracking);
|
||||
if (result) {
|
||||
mIsCollectionStarted = true;
|
||||
mEnableFullTracking = enableFullTracking;
|
||||
mStartedCollection = true;
|
||||
mStartedFullTracking = enableFullTracking;
|
||||
return RemoteListenerHelper.RESULT_SUCCESS;
|
||||
} else {
|
||||
return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
|
||||
@@ -89,7 +107,7 @@ public abstract class GnssMeasurementsProvider
|
||||
protected void unregisterFromService() {
|
||||
boolean stopped = mNative.stopMeasurementCollection();
|
||||
if (stopped) {
|
||||
mIsCollectionStarted = false;
|
||||
mStartedCollection = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
* @hide
|
||||
*/
|
||||
public abstract class GnssNavigationMessageProvider
|
||||
extends RemoteListenerHelper<IGnssNavigationMessageListener> {
|
||||
extends RemoteListenerHelper<Void, IGnssNavigationMessageListener> {
|
||||
private static final String TAG = "GnssNavigationMessageProvider";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ import android.util.Log;
|
||||
/**
|
||||
* Implementation of a handler for {@link IGnssStatusListener}.
|
||||
*/
|
||||
public abstract class GnssStatusListenerHelper extends RemoteListenerHelper<IGnssStatusListener> {
|
||||
public abstract class GnssStatusListenerHelper extends
|
||||
RemoteListenerHelper<Void, IGnssStatusListener> {
|
||||
private static final String TAG = "GnssStatusListenerHelper";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.server.location;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
@@ -32,9 +33,10 @@ import java.util.Objects;
|
||||
/**
|
||||
* A helper class that handles operations in remote listeners.
|
||||
*
|
||||
* @param <TRequest> the type of request.
|
||||
* @param <TListener> the type of GNSS data listener.
|
||||
*/
|
||||
public abstract class RemoteListenerHelper<TListener extends IInterface> {
|
||||
public abstract class RemoteListenerHelper<TRequest, TListener extends IInterface> {
|
||||
|
||||
protected static final int RESULT_SUCCESS = 0;
|
||||
protected static final int RESULT_NOT_AVAILABLE = 1;
|
||||
@@ -47,7 +49,7 @@ public abstract class RemoteListenerHelper<TListener extends IInterface> {
|
||||
protected final Handler mHandler;
|
||||
private final String mTag;
|
||||
|
||||
private final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>();
|
||||
protected final Map<IBinder, IdentifiedListener> mListenerMap = new HashMap<>();
|
||||
|
||||
protected final Context mContext;
|
||||
protected final AppOpsManager mAppOps;
|
||||
@@ -75,7 +77,8 @@ public abstract class RemoteListenerHelper<TListener extends IInterface> {
|
||||
/**
|
||||
* Adds GNSS data listener {@code listener} with caller identify {@code callerIdentify}.
|
||||
*/
|
||||
public void addListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
|
||||
public void addListener(@Nullable TRequest request, @NonNull TListener listener,
|
||||
CallerIdentity callerIdentity) {
|
||||
Objects.requireNonNull(listener, "Attempted to register a 'null' listener.");
|
||||
IBinder binder = listener.asBinder();
|
||||
synchronized (mListenerMap) {
|
||||
@@ -84,7 +87,7 @@ public abstract class RemoteListenerHelper<TListener extends IInterface> {
|
||||
return;
|
||||
}
|
||||
|
||||
IdentifiedListener identifiedListener = new IdentifiedListener(listener,
|
||||
IdentifiedListener identifiedListener = new IdentifiedListener(request, listener,
|
||||
callerIdentity);
|
||||
mListenerMap.put(binder, identifiedListener);
|
||||
|
||||
@@ -257,14 +260,22 @@ public abstract class RemoteListenerHelper<TListener extends IInterface> {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
private class IdentifiedListener {
|
||||
protected class IdentifiedListener {
|
||||
@Nullable private final TRequest mRequest;
|
||||
private final TListener mListener;
|
||||
private final CallerIdentity mCallerIdentity;
|
||||
|
||||
private IdentifiedListener(@NonNull TListener listener, CallerIdentity callerIdentity) {
|
||||
private IdentifiedListener(@Nullable TRequest request, @NonNull TListener listener,
|
||||
CallerIdentity callerIdentity) {
|
||||
mListener = listener;
|
||||
mRequest = request;
|
||||
mCallerIdentity = callerIdentity;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected TRequest getRequest() {
|
||||
return mRequest;
|
||||
}
|
||||
}
|
||||
|
||||
private class HandlerRunnable implements Runnable {
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.app.AppOpsManager;
|
||||
import android.content.Context;
|
||||
import android.location.GnssCapabilities;
|
||||
import android.location.GnssMeasurementCorrections;
|
||||
import android.location.GnssRequest;
|
||||
import android.location.IBatchedLocationCallback;
|
||||
import android.location.IGnssMeasurementsListener;
|
||||
import android.location.IGnssNavigationMessageListener;
|
||||
@@ -96,15 +97,15 @@ public class GnssManagerService {
|
||||
private final IGpsGeofenceHardware mGpsGeofenceProxy;
|
||||
|
||||
@GuardedBy("mGnssMeasurementsListeners")
|
||||
private final ArrayMap<IBinder, LinkedListener<IGnssMeasurementsListener>>
|
||||
private final ArrayMap<IBinder, LinkedListener<GnssRequest, IGnssMeasurementsListener>>
|
||||
mGnssMeasurementsListeners = new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mGnssNavigationMessageListeners")
|
||||
private final ArrayMap<IBinder, LinkedListener<IGnssNavigationMessageListener>>
|
||||
private final ArrayMap<IBinder, LinkedListener<Void, IGnssNavigationMessageListener>>
|
||||
mGnssNavigationMessageListeners = new ArrayMap<>();
|
||||
|
||||
@GuardedBy("mGnssStatusListeners")
|
||||
private final ArrayMap<IBinder, LinkedListener<IGnssStatusListener>>
|
||||
private final ArrayMap<IBinder, LinkedListener<Void, IGnssStatusListener>>
|
||||
mGnssStatusListeners = new ArrayMap<>();
|
||||
|
||||
@GuardedBy("this")
|
||||
@@ -118,7 +119,8 @@ public class GnssManagerService {
|
||||
@Nullable private IBatchedLocationCallback mGnssBatchingCallback;
|
||||
|
||||
@GuardedBy("mGnssBatchingLock")
|
||||
@Nullable private LinkedListener<IBatchedLocationCallback> mGnssBatchingDeathCallback;
|
||||
@Nullable
|
||||
private LinkedListener<Void, IBatchedLocationCallback> mGnssBatchingDeathCallback;
|
||||
|
||||
@GuardedBy("mGnssBatchingLock")
|
||||
private boolean mGnssBatchingInProgress = false;
|
||||
@@ -272,6 +274,7 @@ public class GnssManagerService {
|
||||
mGnssBatchingCallback = callback;
|
||||
mGnssBatchingDeathCallback =
|
||||
new LinkedListener<>(
|
||||
/* request= */ null,
|
||||
callback,
|
||||
"BatchedLocationCallback",
|
||||
callerIdentity,
|
||||
@@ -356,36 +359,39 @@ public class GnssManagerService {
|
||||
}
|
||||
}
|
||||
|
||||
private <TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
|
||||
Map<IBinder, ? extends LinkedListenerBase> gnssDataListeners,
|
||||
RemoteListenerHelper<TListener> gnssDataProvider,
|
||||
private <TRequest, TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
|
||||
Map<IBinder, LinkedListener<TRequest, TListener>> gnssDataListeners,
|
||||
RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
|
||||
Function<IBinder, TListener> mapBinderToListener,
|
||||
int uid,
|
||||
boolean foreground) {
|
||||
for (Map.Entry<IBinder, ? extends LinkedListenerBase> entry :
|
||||
for (Map.Entry<IBinder, LinkedListener<TRequest, TListener>> entry :
|
||||
gnssDataListeners.entrySet()) {
|
||||
LinkedListenerBase linkedListener = entry.getValue();
|
||||
LinkedListener<TRequest, TListener> linkedListener = entry.getValue();
|
||||
CallerIdentity callerIdentity = linkedListener.getCallerIdentity();
|
||||
TRequest request = linkedListener.getRequest();
|
||||
if (callerIdentity.mUid != uid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
TListener listener = mapBinderToListener.apply(entry.getKey());
|
||||
if (foreground || isThrottlingExempt(callerIdentity)) {
|
||||
gnssDataProvider.addListener(listener, callerIdentity);
|
||||
gnssDataProvider.addListener(request, listener, callerIdentity);
|
||||
} else {
|
||||
gnssDataProvider.removeListener(listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private <TListener extends IInterface> boolean addGnssDataListenerLocked(
|
||||
private <TListener extends IInterface, TRequest> boolean addGnssDataListenerLocked(
|
||||
@Nullable TRequest request,
|
||||
TListener listener,
|
||||
String packageName,
|
||||
@Nullable String featureId,
|
||||
@NonNull String listenerIdentifier,
|
||||
RemoteListenerHelper<TListener> gnssDataProvider,
|
||||
ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners,
|
||||
RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
|
||||
ArrayMap<IBinder,
|
||||
LinkedListener<TRequest, TListener>> gnssDataListeners,
|
||||
Consumer<TListener> binderDeathCallback) {
|
||||
mContext.enforceCallingPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
|
||||
|
||||
@@ -395,7 +401,7 @@ public class GnssManagerService {
|
||||
|
||||
CallerIdentity callerIdentity = new CallerIdentity(Binder.getCallingUid(),
|
||||
Binder.getCallingPid(), packageName, featureId, listenerIdentifier);
|
||||
LinkedListener<TListener> linkedListener = new LinkedListener<>(listener,
|
||||
LinkedListener<TRequest, TListener> linkedListener = new LinkedListener<>(request, listener,
|
||||
listenerIdentifier, callerIdentity, binderDeathCallback);
|
||||
IBinder binder = listener.asBinder();
|
||||
if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) {
|
||||
@@ -419,15 +425,15 @@ public class GnssManagerService {
|
||||
}
|
||||
if (mAppForegroundHelper.isAppForeground(callerIdentity.mUid)
|
||||
|| isThrottlingExempt(callerIdentity)) {
|
||||
gnssDataProvider.addListener(listener, callerIdentity);
|
||||
gnssDataProvider.addListener(request, listener, callerIdentity);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private <TListener extends IInterface> void removeGnssDataListenerLocked(
|
||||
private <TRequest, TListener extends IInterface> void removeGnssDataListenerLocked(
|
||||
TListener listener,
|
||||
RemoteListenerHelper<TListener> gnssDataProvider,
|
||||
ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners) {
|
||||
RemoteListenerHelper<TRequest, TListener> gnssDataProvider,
|
||||
ArrayMap<IBinder, LinkedListener<TRequest, TListener>> gnssDataListeners) {
|
||||
if (gnssDataProvider == null) {
|
||||
Log.e(
|
||||
TAG,
|
||||
@@ -437,7 +443,7 @@ public class GnssManagerService {
|
||||
}
|
||||
|
||||
IBinder binder = listener.asBinder();
|
||||
LinkedListener<TListener> linkedListener =
|
||||
LinkedListener<TRequest, TListener> linkedListener =
|
||||
gnssDataListeners.remove(binder);
|
||||
if (linkedListener == null) {
|
||||
return;
|
||||
@@ -467,6 +473,7 @@ public class GnssManagerService {
|
||||
@Nullable String featureId) {
|
||||
synchronized (mGnssStatusListeners) {
|
||||
return addGnssDataListenerLocked(
|
||||
/* request= */ null,
|
||||
listener,
|
||||
packageName,
|
||||
featureId,
|
||||
@@ -489,11 +496,17 @@ public class GnssManagerService {
|
||||
/**
|
||||
* Adds a GNSS measurements listener.
|
||||
*/
|
||||
public boolean addGnssMeasurementsListener(
|
||||
IGnssMeasurementsListener listener, String packageName, @Nullable String featureId,
|
||||
public boolean addGnssMeasurementsListener(@Nullable GnssRequest request,
|
||||
IGnssMeasurementsListener listener, String packageName,
|
||||
@Nullable String featureId,
|
||||
@NonNull String listenerIdentifier) {
|
||||
if (request != null && request.isFullTracking()) {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.LOCATION_HARDWARE,
|
||||
null);
|
||||
}
|
||||
synchronized (mGnssMeasurementsListeners) {
|
||||
return addGnssDataListenerLocked(
|
||||
request,
|
||||
listener,
|
||||
packageName,
|
||||
featureId,
|
||||
@@ -538,6 +551,7 @@ public class GnssManagerService {
|
||||
@Nullable String featureId, @NonNull String listenerIdentifier) {
|
||||
synchronized (mGnssNavigationMessageListeners) {
|
||||
return addGnssDataListenerLocked(
|
||||
/* request= */ null,
|
||||
listener,
|
||||
packageName,
|
||||
featureId,
|
||||
|
||||
@@ -39,6 +39,7 @@ import android.location.GnssClock;
|
||||
import android.location.GnssMeasurementCorrections;
|
||||
import android.location.GnssMeasurementsEvent;
|
||||
import android.location.GnssNavigationMessage;
|
||||
import android.location.GnssRequest;
|
||||
import android.location.GnssSingleSatCorrection;
|
||||
import android.location.IBatchedLocationCallback;
|
||||
import android.location.IGnssMeasurementsListener;
|
||||
@@ -563,7 +564,7 @@ public class GnssManagerServiceTest {
|
||||
|
||||
assertThrows(SecurityException.class,
|
||||
() -> mGnssManagerService.addGnssMeasurementsListener(
|
||||
mockGnssMeasurementsListener,
|
||||
new GnssRequest.Builder().build(), mockGnssMeasurementsListener,
|
||||
"com.android.server", "abcd123", "TestGnssMeasurementsListener"));
|
||||
|
||||
mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
|
||||
@@ -580,8 +581,11 @@ public class GnssManagerServiceTest {
|
||||
|
||||
enableLocationPermissions();
|
||||
|
||||
assertThat(mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener,
|
||||
"com.android.server", "abcd123", "TestGnssMeasurementsListener")).isEqualTo(true);
|
||||
assertThat(mGnssManagerService.addGnssMeasurementsListener(
|
||||
new GnssRequest.Builder().build(),
|
||||
mockGnssMeasurementsListener,
|
||||
"com.android.server", "abcd123",
|
||||
"TestGnssMeasurementsListener")).isEqualTo(true);
|
||||
|
||||
mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
|
||||
verify(mockGnssMeasurementsListener, times(1)).onGnssMeasurementsReceived(
|
||||
@@ -626,8 +630,10 @@ public class GnssManagerServiceTest {
|
||||
|
||||
enableLocationPermissions();
|
||||
|
||||
mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener,
|
||||
"com.android.server", "abcd123", "TestGnssMeasurementsListener");
|
||||
mGnssManagerService.addGnssMeasurementsListener(new GnssRequest.Builder().build(),
|
||||
mockGnssMeasurementsListener,
|
||||
"com.android.server", "abcd123",
|
||||
"TestGnssMeasurementsListener");
|
||||
|
||||
disableLocationPermissions();
|
||||
|
||||
@@ -648,8 +654,10 @@ public class GnssManagerServiceTest {
|
||||
|
||||
enableLocationPermissions();
|
||||
|
||||
mGnssManagerService.addGnssMeasurementsListener(mockGnssMeasurementsListener,
|
||||
"com.android.server", "abcd123", "TestGnssMeasurementsListener");
|
||||
mGnssManagerService.addGnssMeasurementsListener(new GnssRequest.Builder().build(),
|
||||
mockGnssMeasurementsListener,
|
||||
"com.android.server", "abcd123",
|
||||
"TestGnssMeasurementsListener");
|
||||
|
||||
disableLocationPermissions();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user