diff --git a/core/java/com/android/internal/listeners/ListenerExecutor.java b/core/java/com/android/internal/listeners/ListenerExecutor.java index 9979e6056f500..7d9b9c96b3a0e 100644 --- a/core/java/com/android/internal/listeners/ListenerExecutor.java +++ b/core/java/com/android/internal/listeners/ListenerExecutor.java @@ -38,71 +38,86 @@ public interface ListenerExecutor { void operate(TListener listener) throws Exception; /** - * Called before this operation is to be run. Some operations may be canceled before they - * are run, in which case this method may not be called. {@link #onPostExecute(boolean)} - * will only be run if this method was run. This callback is invoked on the calling thread. + * Called before this operation is to be run. An operation may be canceled before it is run, + * in which case this method may not be invoked. {@link #onPostExecute(boolean)} will only + * be invoked if this method was previously invoked. This callback is invoked on the + * calling thread. */ default void onPreExecute() {} /** * Called if the operation fails while running. Will not be invoked in the event of a - * RuntimeException, which will propagate normally. Implementations of - * {@link ListenerExecutor} have the option to override - * {@link ListenerExecutor#onOperationFailure(ListenerOperation, Exception)} instead to - * intercept failures at the class level. This callback is invoked on the executor thread. + * unchecked exception, which will propagate normally. This callback is invoked on the + * executor thread. */ - default void onFailure(Exception e) { - // implementations should handle any exceptions that may be thrown - throw new AssertionError(e); - } + default void onFailure(Exception e) {} /** - * Called after the operation is run. This method will always be called if - * {@link #onPreExecute()} is called. Success implies that the operation was run to - * completion with no failures. This callback may be invoked on the calling thread or - * executor thread. + * Called after the operation may have been run. Will always be invoked for every operation + * which has previously had {@link #onPreExecute()} invoked. Success implies that the + * operation was run to completion with no failures. If {@code success} is true, this + * callback will always be invoked on the executor thread. If {@code success} is false, this + * callback may be invoked on the calling thread or executor thread. */ default void onPostExecute(boolean success) {} /** - * Called after this operation is complete (which does not imply that it was necessarily - * run). Will always be called once per operation, no matter if the operation was run or - * not. Success implies that the operation was run to completion with no failures. This - * callback may be invoked on the calling thread or executor thread. + * Will always be called once for every operation submitted to + * {@link #executeSafely(Executor, Supplier, ListenerOperation)}, no matter if the operation + * was run or not. This method is always invoked last, after every other callback. Success + * implies that the operation was run to completion with no failures. If {@code success} + * is true, this callback will always be invoked on the executor thread. If {@code success} + * is false, this callback may be invoked on the calling thread or executor thread. */ default void onComplete(boolean success) {} } /** - * May be override to handle operation failures at a class level. Will not be invoked in the - * event of a RuntimeException, which will propagate normally. This callback is invoked on the - * executor thread. + * An callback for listener operation failure. + * + * @param listener operation type */ - default void onOperationFailure(ListenerOperation operation, - Exception exception) { - operation.onFailure(exception); + interface FailureCallback> { + + /** + * Called if a listener operation fails while running with a checked exception. This + * callback is invoked on the executor thread. + */ + void onFailure(TListenerOperation operation, Exception exception); + } + + /** + * See {@link #executeSafely(Executor, Supplier, ListenerOperation, FailureCallback)}. + */ + default void executeSafely(Executor executor, Supplier listenerSupplier, + @Nullable ListenerOperation operation) { + executeSafely(executor, listenerSupplier, operation, null); } /** * Executes the given listener operation on the given executor, using the provided listener * supplier. If the supplier returns a null value, or a value during the operation that does not * match the value prior to the operation, then the operation is considered canceled. If a null - * operation is supplied, nothing happens. + * operation is supplied, nothing happens. If a failure callback is supplied, this will be + * invoked on the executor thread in the event a checked exception is thrown from the listener + * operation. */ - default void executeSafely(Executor executor, Supplier listenerSupplier, - @Nullable ListenerOperation operation) { + default > void executeSafely( + Executor executor, Supplier listenerSupplier, + @Nullable TListenerOperation operation, + @Nullable FailureCallback failureCallback) { if (operation == null) { return; } + TListener listener = listenerSupplier.get(); + if (listener == null) { + return; + } + boolean executing = false; boolean preexecute = false; try { - TListener listener = listenerSupplier.get(); - if (listener == null) { - return; - } - operation.onPreExecute(); preexecute = true; executor.execute(() -> { @@ -116,7 +131,10 @@ public interface ListenerExecutor { if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { - onOperationFailure(operation, e); + operation.onFailure(e); + if (failureCallback != null) { + failureCallback.onFailure(operation, e); + } } } finally { operation.onPostExecute(success); diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index 911b9ab9c9737..0521b10a2530a 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -475,7 +475,7 @@ public final class LocationRequest implements Parcelable { /** * Returns the minimum update interval. If location updates are available faster than the - * request interval then locations will only be delivered if the minimum update interval has + * request interval then locations will only be updated if the minimum update interval has * expired since the last location update. * *

Note: Some allowance for jitter is already built into the @@ -951,9 +951,9 @@ public final class LocationRequest implements Parcelable { /** * Sets an explicit minimum update interval. If location updates are available faster than - * the request interval then locations will only be delivered if the minimum update interval - * has expired since the last location update. Defaults to no explicit minimum update - * interval set, which means the minimum update interval is the same as the interval. + * the request interval then an update will only occur if the minimum update interval has + * expired since the last location update. Defaults to no explicit minimum update interval + * set, which means the minimum update interval is the same as the interval. * *

Note: Some allowance for jitter is already built into the * minimum update interval, so you need not worry about updates blocked simply because they @@ -972,7 +972,7 @@ public final class LocationRequest implements Parcelable { /** * Clears an explicitly set minimum update interval and reverts to an implicit minimum - * update interval (ie, the minimum update interval is same value as the interval). + * update interval (ie, the minimum update interval is the same value as the interval). */ public @NonNull Builder clearMinUpdateIntervalMillis() { mMinUpdateIntervalMillis = IMPLICIT_MIN_UPDATE_INTERVAL; @@ -980,8 +980,8 @@ public final class LocationRequest implements Parcelable { } /** - * Sets the minimum update distance between delivered locations. If a potential location - * update is closer to the last delivered location than the minimum update distance, then + * Sets the minimum update distance between location updates. If a potential location + * update is closer to the last location update than the minimum update distance, then * the potential location update will not occur. Defaults to 0, which represents no minimum * update distance. */ diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java index 06e7a762d28e8..56982a81f83b6 100644 --- a/services/core/java/com/android/server/location/AbstractLocationProvider.java +++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java @@ -16,8 +16,6 @@ package com.android.server.location; -import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable; - import android.annotation.Nullable; import android.location.Location; import android.location.util.identity.CallerIdentity; @@ -342,8 +340,7 @@ public abstract class AbstractLocationProvider { */ public final void setRequest(ProviderRequest request) { // all calls into the provider must be moved onto the provider thread to prevent deadlock - mExecutor.execute(obtainRunnable(AbstractLocationProvider::onSetRequest, this, request) - .recycleOnUse()); + mExecutor.execute(() -> onSetRequest(request)); } /** @@ -356,13 +353,7 @@ public abstract class AbstractLocationProvider { */ public final void sendExtraCommand(int uid, int pid, String command, Bundle extras) { // all calls into the provider must be moved onto the provider thread to prevent deadlock - - // the integer boxing done here likely cancels out any gains from removing lambda - // allocation, but since this an infrequently used api with no real performance needs, we - // we use pooled lambdas anyways for consistency. - mExecutor.execute( - obtainRunnable(AbstractLocationProvider::onExtraCommand, this, uid, pid, command, - extras).recycleOnUse()); + mExecutor.execute(() -> onExtraCommand(uid, pid, command, extras)); } /** diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java index e224a9d851158..2a5e035e0c72b 100644 --- a/services/core/java/com/android/server/location/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -29,6 +29,7 @@ import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY; import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF; import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; +import static com.android.internal.location.ProviderRequest.EMPTY_REQUEST; import static com.android.server.location.LocationManagerService.D; import static com.android.server.location.LocationManagerService.TAG; import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; @@ -77,6 +78,7 @@ import android.util.SparseBooleanArray; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; +import com.android.internal.listeners.ListenerExecutor.ListenerOperation; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; import com.android.internal.util.Preconditions; @@ -111,7 +113,8 @@ import java.util.List; import java.util.Objects; class LocationProviderManager extends - ListenerMultiplexer implements AbstractLocationProvider.Listener { @@ -121,16 +124,16 @@ class LocationProviderManager extends private static final String WAKELOCK_TAG = "*location*"; private static final long WAKELOCK_TIMEOUT_MS = 30 * 1000; - // maximum interval to be considered "high power" request + // max interval to be considered "high power" request private static final long MAX_HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000; - // maximum age of a location before it is no longer considered "current" + // max age of a location before it is no longer considered "current" private static final long MAX_CURRENT_LOCATION_AGE_MS = 10 * 1000; // max timeout allowed for getting the current location private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000; - // maximum jitter allowed for fastest interval evaluation + // max jitter allowed for fastest interval evaluation private static final int MAX_FASTEST_INTERVAL_JITTER_MS = 100; protected interface LocationTransport { @@ -214,8 +217,15 @@ class LocationProviderManager extends } } - protected abstract class Registration extends - RemoteListenerRegistration { + protected interface LocationListenerOperation extends ListenerOperation { + /** + * Must be implemented to return the location this operation intends to deliver. + */ + Location getLocation(); + } + + protected abstract class Registration extends RemoteListenerRegistration { @PermissionLevel protected final int mPermissionLevel; private final WorkSource mWorkSource; @@ -226,9 +236,11 @@ class LocationProviderManager extends private LocationRequest mProviderLocationRequest; private boolean mIsUsingHighPower; + @Nullable private Location mLastLocation = null; + protected Registration(LocationRequest request, CallerIdentity identity, LocationTransport transport, @PermissionLevel int permissionLevel) { - super(TAG, Objects.requireNonNull(request), identity, transport); + super(Objects.requireNonNull(request), identity, transport); Preconditions.checkArgument(permissionLevel > PERMISSION_NONE); mPermissionLevel = permissionLevel; @@ -291,7 +303,11 @@ class LocationProviderManager extends protected void onProviderListenerUnregister() {} @Override - protected final ListenerOperation onActive() { + protected final LocationListenerOperation onActive() { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + if (!getRequest().isHiddenFromAppOps()) { mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey()); } @@ -300,7 +316,11 @@ class LocationProviderManager extends } @Override - protected final ListenerOperation onInactive() { + protected final LocationListenerOperation onInactive() { + if (Build.IS_DEBUGGABLE) { + Preconditions.checkState(Thread.holdsLock(mLock)); + } + onHighPowerUsageChanged(); if (!getRequest().isHiddenFromAppOps()) { mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey()); @@ -308,19 +328,15 @@ class LocationProviderManager extends return null; } - @Override - public final void onOperationFailure(ListenerOperation operation, - Exception e) { - synchronized (mLock) { - super.onOperationFailure(operation, e); - } - } - @Override public final LocationRequest getRequest() { return mProviderLocationRequest; } + public final Location getLastDeliveredLocation() { + return mLastLocation; + } + public final boolean isForeground() { return mForeground; } @@ -480,8 +496,17 @@ class LocationProviderManager extends return mLocationManagerInternal.isProvider(null, getIdentity()); } + @GuardedBy("mLock") + @Override + protected final LocationListenerOperation onExecuteOperation( + LocationListenerOperation operation) { + mLastLocation = operation.getLocation(); + return super.onExecuteOperation(operation); + } + + @GuardedBy("mLock") @Nullable - abstract ListenerOperation acceptLocationChange(Location fineLocation); + abstract LocationListenerOperation acceptLocationChange(Location fineLocation); @Override public String toString() { @@ -514,7 +539,6 @@ class LocationProviderManager extends private final PowerManager.WakeLock mWakeLock; private volatile ProviderTransport mProviderTransport; - @Nullable private Location mLastLocation = null; private int mNumLocationsDelivered = 0; private long mExpirationRealtimeMs = Long.MAX_VALUE; @@ -606,7 +630,7 @@ class LocationProviderManager extends @GuardedBy("mLock") @Nullable @Override - ListenerOperation acceptLocationChange(Location fineLocation) { + LocationListenerOperation acceptLocationChange(Location fineLocation) { if (Build.IS_DEBUGGABLE) { Preconditions.checkState(Thread.holdsLock(mLock)); } @@ -631,11 +655,12 @@ class LocationProviderManager extends throw new AssertionError(); } - if (mLastLocation != null) { + Location lastDeliveredLocation = getLastDeliveredLocation(); + if (lastDeliveredLocation != null) { // check fastest interval long deltaMs = NANOSECONDS.toMillis( location.getElapsedRealtimeNanos() - - mLastLocation.getElapsedRealtimeNanos()); + - lastDeliveredLocation.getElapsedRealtimeNanos()); if (deltaMs < getRequest().getMinUpdateIntervalMillis() - MAX_FASTEST_INTERVAL_JITTER_MS) { return null; @@ -643,7 +668,7 @@ class LocationProviderManager extends // check smallest displacement double smallestDisplacementM = getRequest().getMinUpdateDistanceMeters(); - if (smallestDisplacementM > 0.0 && location.distanceTo(mLastLocation) + if (smallestDisplacementM > 0.0 && location.distanceTo(lastDeliveredLocation) <= smallestDisplacementM) { return null; } @@ -658,10 +683,12 @@ class LocationProviderManager extends return null; } - // update last location - mLastLocation = location; + return new LocationListenerOperation() { + @Override + public Location getLocation() { + return location; + } - return new ListenerOperation() { @Override public void onPreExecute() { // don't acquire a wakelock for mock locations to prevent abuse @@ -720,8 +747,12 @@ class LocationProviderManager extends // we choose not to hold a wakelock for provider enabled changed events executeSafely(getExecutor(), () -> mProviderTransport, - listener -> listener.deliverOnProviderEnabledChanged(mName, enabled)); + listener -> listener.deliverOnProviderEnabledChanged(mName, enabled), + this::onProviderOperationFailure); } + + protected abstract void onProviderOperationFailure( + ListenerOperation operation, Exception exception); } protected final class LocationListenerRegistration extends LocationRegistration implements @@ -748,6 +779,28 @@ class LocationProviderManager extends ((IBinder) getKey()).unlinkToDeath(this, 0); } + @Override + protected void onProviderOperationFailure(ListenerOperation operation, + Exception exception) { + onTransportFailure(exception); + } + + @Override + public void onOperationFailure(LocationListenerOperation operation, Exception exception) { + onTransportFailure(exception); + } + + private void onTransportFailure(Exception exception) { + if (exception instanceof RemoteException) { + Log.w(TAG, "registration " + this + " removed", exception); + synchronized (mLock) { + remove(); + } + } else { + throw new AssertionError(exception); + } + } + @Override public void binderDied() { try { @@ -787,6 +840,28 @@ class LocationProviderManager extends ((PendingIntent) getKey()).unregisterCancelListener(this); } + @Override + protected void onProviderOperationFailure(ListenerOperation operation, + Exception exception) { + onTransportFailure(exception); + } + + @Override + public void onOperationFailure(LocationListenerOperation operation, Exception exception) { + onTransportFailure(exception); + } + + private void onTransportFailure(Exception exception) { + if (exception instanceof PendingIntent.CanceledException) { + Log.w(TAG, "registration " + this + " removed", exception); + synchronized (mLock) { + remove(); + } + } else { + throw new AssertionError(exception); + } + } + @Override public void onCancelled(PendingIntent intent) { synchronized (mLock) { @@ -838,13 +913,17 @@ class LocationProviderManager extends 0, this, FgThread.getHandler(), getWorkSource()); } - // start listening for provider enabled/disabled events - addEnabledListener(this); + // if this request is ignoring location settings, then we don't want to immediately fail + // it if the provider is disabled or becomes disabled. + if (!getRequest().isLocationSettingsIgnored()) { + // start listening for provider enabled/disabled events + addEnabledListener(this); - // if the provider is currently disabled fail immediately - int userId = getIdentity().getUserId(); - if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) { - deliverLocation(null); + // if the provider is currently disabled fail immediately + int userId = getIdentity().getUserId(); + if (!getRequest().isLocationSettingsIgnored() && !isEnabled(userId)) { + deliverLocation(null); + } } } @@ -880,7 +959,7 @@ class LocationProviderManager extends @GuardedBy("mLock") @Nullable @Override - ListenerOperation acceptLocationChange(@Nullable Location fineLocation) { + LocationListenerOperation acceptLocationChange(@Nullable Location fineLocation) { if (Build.IS_DEBUGGABLE) { Preconditions.checkState(Thread.holdsLock(mLock)); } @@ -909,19 +988,35 @@ class LocationProviderManager extends } } - return listener -> { - // if delivering to the same process, make a copy of the location first (since - // location is mutable) - Location deliveryLocation = location; - if (getIdentity().getPid() == Process.myPid() && location != null) { - deliveryLocation = new Location(location); + return new LocationListenerOperation() { + @Override + public Location getLocation() { + return location; } - // we currently don't hold a wakelock for getCurrentLocation deliveries - listener.deliverOnLocationChanged(deliveryLocation, null); + @Override + public void operate(LocationTransport listener) { + // if delivering to the same process, make a copy of the location first (since + // location is mutable) + Location deliveryLocation = location; + if (getIdentity().getPid() == Process.myPid() && location != null) { + deliveryLocation = new Location(location); + } - synchronized (mLock) { - remove(); + // we currently don't hold a wakelock for getCurrentLocation deliveries + try { + listener.deliverOnLocationChanged(deliveryLocation, null); + } catch (Exception exception) { + if (exception instanceof RemoteException) { + Log.w(TAG, "registration " + this + " failed", exception); + } else { + throw new AssertionError(exception); + } + } + + synchronized (mLock) { + remove(); + } } }; } @@ -1054,6 +1149,11 @@ class LocationProviderManager extends mProvider = new MockableLocationProvider(mLock, this); } + @Override + public String getTag() { + return TAG; + } + public void startManager() { synchronized (mLock) { mStarted = true; @@ -1260,8 +1360,7 @@ class LocationProviderManager extends /** * This function does not perform any permissions or safety checks, by calling it you are - * committing to performing all applicable checks yourself. Prefer - * {@link #getLastLocation(CallerIdentity, int, boolean)} where possible. + * committing to performing all applicable checks yourself. */ @Nullable public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel, @@ -1554,7 +1653,8 @@ class LocationProviderManager extends @GuardedBy("mLock") @Override - protected boolean registerWithService(ProviderRequest mergedRequest) { + protected boolean registerWithService(ProviderRequest mergedRequest, + Collection registrations) { if (Build.IS_DEBUGGABLE) { Preconditions.checkState(Thread.holdsLock(mLock)); } @@ -1566,8 +1666,8 @@ class LocationProviderManager extends @GuardedBy("mLock") @Override protected boolean reregisterWithService(ProviderRequest oldRequest, - ProviderRequest newRequest) { - return registerWithService(newRequest); + ProviderRequest newRequest, Collection registrations) { + return registerWithService(newRequest, registrations); } @GuardedBy("mLock") @@ -1576,7 +1676,8 @@ class LocationProviderManager extends if (Build.IS_DEBUGGABLE) { Preconditions.checkState(Thread.holdsLock(mLock)); } - mProvider.setRequest(ProviderRequest.EMPTY_REQUEST); + + mProvider.setRequest(EMPTY_REQUEST); } @GuardedBy("mLock") @@ -1628,7 +1729,7 @@ class LocationProviderManager extends @GuardedBy("mLock") @Override - protected ProviderRequest mergeRequests(Collection registrations) { + protected ProviderRequest mergeRegistrations(Collection registrations) { if (Build.IS_DEBUGGABLE) { Preconditions.checkState(Thread.holdsLock(mLock)); } @@ -1814,9 +1915,7 @@ class LocationProviderManager extends } // attempt listener delivery - deliverToListeners(registration -> { - return registration.acceptLocationChange(location); - }); + deliverToListeners(registration -> registration.acceptLocationChange(location)); } @GuardedBy("mLock") @@ -1951,7 +2050,8 @@ class LocationProviderManager extends public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) { synchronized (mLock) { - ipw.print(mName + " provider"); + ipw.print(mName); + ipw.print(" provider"); if (mProvider.isMock()) { ipw.print(" [mock]"); } @@ -1963,12 +2063,15 @@ class LocationProviderManager extends int[] userIds = mUserInfoHelper.getRunningUserIds(); for (int userId : userIds) { if (userIds.length != 1) { - ipw.println("user " + userId + ":"); + ipw.print("user "); + ipw.print(userId); + ipw.println(":"); ipw.increaseIndent(); } - ipw.println( - "last location=" + getLastLocationUnsafe(userId, PERMISSION_FINE, false)); - ipw.println("enabled=" + isEnabled(userId)); + ipw.print("last location="); + ipw.println(getLastLocationUnsafe(userId, PERMISSION_FINE, false)); + ipw.print("enabled="); + ipw.println(isEnabled(userId)); if (userIds.length != 1) { ipw.decreaseIndent(); } diff --git a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java index 5133683b06254..afeb6444c40b2 100644 --- a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java +++ b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java @@ -64,7 +64,7 @@ class PassiveLocationProviderManager extends LocationProviderManager { } @Override - protected ProviderRequest mergeRequests(Collection registrations) { + protected ProviderRequest mergeRegistrations(Collection registrations) { ProviderRequest.Builder providerRequest = new ProviderRequest.Builder() .setIntervalMillis(0); diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java index 7f02b2a807c3d..58e6d593ed2d3 100644 --- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java +++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java @@ -41,6 +41,7 @@ import android.stats.location.LocationStatsEnums; import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; +import com.android.internal.listeners.ListenerExecutor.ListenerOperation; import com.android.server.PendingIntentUtils; import com.android.server.location.LocationPermissions; import com.android.server.location.listeners.ListenerMultiplexer; @@ -59,7 +60,7 @@ import java.util.Objects; * Manages all geofences. */ public class GeofenceManager extends - ListenerMultiplexer, GeofenceManager.GeofenceRegistration, LocationRequest> implements LocationListener { @@ -94,7 +95,7 @@ public class GeofenceManager extends protected GeofenceRegistration(Geofence geofence, CallerIdentity identity, PendingIntent pendingIntent) { - super(TAG, geofence, identity, pendingIntent); + super(geofence, identity, pendingIntent); mCenter = new Location(""); mCenter.setLatitude(geofence.getLatitude()); @@ -273,6 +274,11 @@ public class GeofenceManager extends mLocationUsageLogger = injector.getLocationUsageLogger(); } + @Override + public String getTag() { + return TAG; + } + private LocationManager getLocationManager() { synchronized (mLock) { if (mLocationManager == null) { @@ -372,7 +378,8 @@ public class GeofenceManager extends } @Override - protected boolean registerWithService(LocationRequest locationRequest) { + protected boolean registerWithService(LocationRequest locationRequest, + Collection registrations) { getLocationManager().requestLocationUpdates(FUSED_PROVIDER, locationRequest, DIRECT_EXECUTOR, this); return true; @@ -387,7 +394,7 @@ public class GeofenceManager extends } @Override - protected LocationRequest mergeRequests(Collection registrations) { + protected LocationRequest mergeRegistrations(Collection registrations) { Location location = getLastLocation(); long realtimeMs = SystemClock.elapsedRealtime(); diff --git a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java index 5ddc6a1e401d5..b99e26988abf8 100644 --- a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java @@ -28,6 +28,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.location.util.Injector; +import java.util.Collection; import java.util.List; /** @@ -59,7 +60,8 @@ public class GnssAntennaInfoProvider extends } @Override - protected boolean registerWithService(Void ignored) { + protected boolean registerWithService(Void ignored, + Collection registrations) { Preconditions.checkState(mNative.isAntennaInfoSupported()); if (mNative.startAntennaInfoListening()) { diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java index a9fdacca9a060..a830e2fe79739 100644 --- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java @@ -26,11 +26,13 @@ import android.location.LocationManagerInternal; import android.location.LocationManagerInternal.ProviderEnabledListener; import android.location.util.identity.CallerIdentity; import android.os.Binder; +import android.os.Build; import android.os.IBinder; import android.os.IInterface; import android.os.Process; import android.util.ArraySet; +import com.android.internal.listeners.ListenerExecutor.ListenerOperation; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.location.listeners.BinderListenerRegistration; @@ -43,6 +45,7 @@ import com.android.server.location.util.UserInfoHelper; import com.android.server.location.util.UserInfoHelper.UserListener; import java.io.PrintWriter; +import java.util.Collection; import java.util.Objects; /** @@ -52,14 +55,15 @@ import java.util.Objects; * Listeners must be registered with the associated IBinder as the key, if the IBinder dies, the * registration will automatically be removed. * - * @param request type - * @param listener type - * @param merged request type + * @param request type + * @param listener type + * @param merged registration type */ public abstract class GnssListenerMultiplexer extends - ListenerMultiplexer.GnssListenerRegistration, TMergedRequest> { + TMergedRegistration> extends + ListenerMultiplexer, + GnssListenerMultiplexer + .GnssListenerRegistration, TMergedRegistration> { /** * Registration object for GNSS listeners. @@ -74,11 +78,11 @@ public abstract class GnssListenerMultiplexer getOwner() { + protected GnssListenerMultiplexer getOwner() { return GnssListenerMultiplexer.this; } @@ -199,6 +203,11 @@ public abstract class GnssListenerMultiplexer gnssListenerRegistrations) { + if (Build.IS_DEBUGGABLE) { + for (GnssListenerRegistration registration : gnssListenerRegistrations) { + Preconditions.checkState(registration.getRequest() == null); + } + } + + return null; + } + @Override protected void onRegister() { if (!isServiceSupported()) { diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java index 37db02337a2fb..2faa15fac42f2 100644 --- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java @@ -76,7 +76,8 @@ public class GnssMeasurementsProvider extends } @Override - protected boolean registerWithService(Boolean fullTrackingRequest) { + protected boolean registerWithService(Boolean fullTrackingRequest, + Collection registrations) { Preconditions.checkState(mNative.isMeasurementSupported()); if (mNative.startMeasurementCollection(fullTrackingRequest)) { @@ -121,7 +122,7 @@ public class GnssMeasurementsProvider extends } @Override - protected Boolean mergeRequests(Collection registrations) { + protected Boolean mergeRegistrations(Collection registrations) { if (mSettingsHelper.isGnssMeasurementsFullTrackingEnabled()) { return true; } diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java index 7dcffc664f522..861abc3ef5927 100644 --- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java @@ -30,6 +30,8 @@ import com.android.internal.util.Preconditions; import com.android.server.location.util.AppOpsHelper; import com.android.server.location.util.Injector; +import java.util.Collection; + /** * An base implementation for GPS navigation messages provider. * It abstracts out the responsibility of handling listeners, while still allowing technology @@ -61,7 +63,8 @@ public class GnssNavigationMessageProvider extends } @Override - protected boolean registerWithService(Void ignored) { + protected boolean registerWithService(Void ignored, + Collection registrations) { Preconditions.checkState(mNative.isNavigationMessageSupported()); if (mNative.startNavigationMessageCollection()) { diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java index 68813b3e0777b..248d38a940ddf 100644 --- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java @@ -31,6 +31,8 @@ import com.android.server.location.util.AppOpsHelper; import com.android.server.location.util.Injector; import com.android.server.location.util.LocationUsageLogger; +import java.util.Collection; + /** * Implementation of a handler for {@link IGnssStatusListener}. */ @@ -51,7 +53,8 @@ public class GnssStatusProvider extends GnssListenerMultiplexer registrations) { if (D) { Log.d(TAG, "starting gnss status"); } diff --git a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java index 58aabdad056ff..bc675ceda9709 100644 --- a/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/BinderListenerRegistration.java @@ -23,6 +23,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Log; +import com.android.internal.listeners.ListenerExecutor.ListenerOperation; + /** * A registration that works with IBinder keys, and registers a DeathListener to automatically * remove the registration if the binder dies. The key for this registration must either be an @@ -32,7 +34,8 @@ import android.util.Log; * @param listener type */ public abstract class BinderListenerRegistration extends - RemoteListenerRegistration implements Binder.DeathRecipient { + RemoteListenerRegistration> implements + Binder.DeathRecipient { /** * Interface to allow binder retrieval when keys are not themselves IBinders. @@ -44,9 +47,9 @@ public abstract class BinderListenerRegistration extends IBinder getBinder(); } - protected BinderListenerRegistration(String tag, @Nullable TRequest request, - CallerIdentity callerIdentity, TListener listener) { - super(tag, request, callerIdentity, listener); + protected BinderListenerRegistration(@Nullable TRequest request, CallerIdentity callerIdentity, + TListener listener) { + super(request, callerIdentity, listener); } @Override @@ -77,11 +80,21 @@ public abstract class BinderListenerRegistration extends */ protected void onBinderListenerUnregister() {} + @Override + public void onOperationFailure(ListenerOperation operation, Exception e) { + if (e instanceof RemoteException) { + Log.w(getOwner().getTag(), "registration " + this + " removed", e); + remove(); + } else { + super.onOperationFailure(operation, e); + } + } + @Override public void binderDied() { try { - if (Log.isLoggable(mTag, Log.DEBUG)) { - Log.d(mTag, "binder registration " + getIdentity() + " died"); + if (Log.isLoggable(getOwner().getTag(), Log.DEBUG)) { + Log.d(getOwner().getTag(), "binder registration " + getIdentity() + " died"); } remove(); } catch (RuntimeException e) { diff --git a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java index feb4fbd1de312..87d668a07d703 100644 --- a/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/listeners/ListenerMultiplexer.java @@ -37,12 +37,20 @@ import java.util.function.Function; import java.util.function.Predicate; /** - * A base class to multiplex client listener registrations within system server. Registrations are - * divided into two categories, active registrations and inactive registrations, as defined by - * {@link #isActive(ListenerRegistration)}. If a registration's active state changes, + * A base class to multiplex client listener registrations within system server. Every listener is + * represented by a registration object which stores all required state for a listener. Keys are + * used to uniquely identify every registration. Listener operations may be executed on + * registrations in order to invoke the represented listener. + * + * Registrations are divided into two categories, active registrations and inactive registrations, + * as defined by {@link #isActive(ListenerRegistration)}. If a registration's active state changes, * {@link #updateRegistrations(Predicate)} must be invoked and return true for any registration * whose active state may have changed. Listeners will only be invoked for active registrations. * + * The set of active registrations is combined into a single merged registration, which is submitted + * to the backing service when necessary in order to register the service. The merged registration + * is updated whenever the set of active registration changes. + * * Callbacks invoked for various changes will always be ordered according to this lifecycle list: * *

    @@ -63,14 +71,16 @@ import java.util.function.Predicate; * {@link #removeRegistration(Object, ListenerRegistration)}, not via any other removal method. This * ensures re-entrant removal does not accidentally remove the incorrect registration. * - * @param key type - * @param request type - * @param listener type - * @param registration type - * @param merged request type + * @param key type + * @param listener type + * @param listener operation type + * @param registration type + * @param merged registration type */ -public abstract class ListenerMultiplexer, TMergedRequest> { +public abstract class ListenerMultiplexer, + TRegistration extends ListenerRegistration, + TMergedRegistration> { @GuardedBy("mRegistrations") private final ArrayMap mRegistrations = new ArrayMap<>(); @@ -88,25 +98,42 @@ public abstract class ListenerMultiplexerIt may seem redundant to pass in the set of active registrations when they + * have already been used to generate the merged request, and indeed, for many implementations + * this parameter can likely simply be ignored. However, some implementations may require access + * to the set of registrations used to generate the merged requestion for further logic even + * after the merged registration has been generated. + * + * @see #mergeRegistrations(Collection) + * @see #reregisterWithService(Object, Object, Collection) */ - protected abstract boolean registerWithService(TMergedRequest newRequest); + protected abstract boolean registerWithService(TMergedRegistration merged, + @NonNull Collection registrations); /** - * Invoked when the service already has a request, and it is being replaced with a new request. - * The default implementation unregisters first, then registers with the new merged request, but - * this may be overridden by subclasses in order to reregister more efficiently. + * Invoked when the service has already been registered with some merged registration, and is + * now being registered with a different merged registration. The default implementation simply + * invokes {@link #registerWithService(Object, Collection)}. + * + * @see #registerWithService(Object, Collection) */ - protected boolean reregisterWithService(TMergedRequest oldRequest, TMergedRequest newRequest) { - unregisterWithService(); - return registerWithService(newRequest); + protected boolean reregisterWithService(TMergedRegistration oldMerged, + TMergedRegistration newMerged, @NonNull Collection registrations) { + return registerWithService(newMerged, registrations); } /** @@ -116,28 +143,23 @@ public abstract class ListenerMultiplexer registrations) { - if (Build.IS_DEBUGGABLE) { - for (TRegistration registration : registrations) { - // if using non-null requests then implementations must override this method - Preconditions.checkState(registration.getRequest() == null); - } - } - - return null; - } + protected abstract @Nullable TMergedRegistration mergeRegistrations( + @NonNull Collection registrations); /** * Invoked when the multiplexer goes from having no registrations to having some registrations. @@ -348,41 +370,42 @@ public abstract class ListenerMultiplexer operation = registration.onActive(); + TListenerOperation operation = registration.onActive(); if (operation != null) { execute(registration, operation); } } else { - ListenerOperation operation = registration.onInactive(); + TListenerOperation operation = registration.onInactive(); if (operation != null) { execute(registration, operation); } @@ -459,14 +482,14 @@ public abstract class ListenerMultiplexer> function) { + @NonNull Function function) { synchronized (mRegistrations) { try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) { final int size = mRegistrations.size(); for (int i = 0; i < size; i++) { TRegistration registration = mRegistrations.valueAt(i); if (registration.isActive()) { - ListenerOperation operation = function.apply(registration); + TListenerOperation operation = function.apply(registration); if (operation != null) { execute(registration, operation); } @@ -483,7 +506,7 @@ public abstract class ListenerMultiplexer operation); * */ - protected final void deliverToListeners(@NonNull ListenerOperation operation) { + protected final void deliverToListeners(@NonNull TListenerOperation operation) { synchronized (mRegistrations) { try (ReentrancyGuard ignored = mReentrancyGuard.acquire()) { final int size = mRegistrations.size(); @@ -502,7 +525,7 @@ public abstract class ListenerMultiplexer operation) { + private void execute(TRegistration registration, TListenerOperation operation) { registration.executeInternal(operation); } @@ -539,10 +562,11 @@ public abstract class ListenerMultiplexer request type - * @param listener type + * @param listener type + * @param listener operation type */ -public class ListenerRegistration implements ListenerExecutor { +public class ListenerRegistration> implements + ListenerExecutor { private final Executor mExecutor; - private final @Nullable TRequest mRequest; private boolean mActive; private volatile @Nullable TListener mListener; - protected ListenerRegistration(Executor executor, @Nullable TRequest request, - TListener listener) { + protected ListenerRegistration(Executor executor, TListener listener) { mExecutor = Objects.requireNonNull(executor); - mRequest = request; mActive = false; mListener = Objects.requireNonNull(listener); } @@ -53,13 +53,6 @@ public class ListenerRegistration implements ListenerExecut return mExecutor; } - /** - * Returns the request associated with this listener, or null if one wasn't supplied. - */ - public @Nullable TRequest getRequest() { - return mRequest; - } - /** * May be overridden by subclasses. Invoked when registration occurs. Invoked while holding the * owning multiplexer's internal lock. @@ -77,7 +70,7 @@ public class ListenerRegistration implements ListenerExecut * returns a non-null operation, that operation will be invoked for the listener. Invoked * while holding the owning multiplexer's internal lock. */ - protected @Nullable ListenerOperation onActive() { + protected @Nullable TListenerOperation onActive() { return null; } @@ -86,7 +79,7 @@ public class ListenerRegistration implements ListenerExecut * a non-null operation, that operation will be invoked for the listener. Invoked while holding * the owning multiplexer's internal lock. */ - protected @Nullable ListenerOperation onInactive() { + protected @Nullable TListenerOperation onInactive() { return null; } @@ -115,21 +108,38 @@ public class ListenerRegistration implements ListenerExecut /** * May be overridden by subclasses, however should rarely be needed. Invoked when the listener * associated with this registration is unregistered, which may occur before the registration - * itself is unregistered. This immediately prevents the listener from being further invoked. + * itself is unregistered. This immediately prevents the listener from being further invoked + * until the registration itself can be finalized and unregistered completely. */ - protected void onListenerUnregister() {}; + protected void onListenerUnregister() {} - final void executeInternal(@NonNull ListenerOperation operation) { - executeSafely(mExecutor, () -> mListener, operation); + /** + * May be overridden by subclasses, however should rarely be needed. Invoked whenever a listener + * operation is submitted for execution, and allows the registration a chance to replace the + * listener operation or perform related bookkeeping. There is no guarantee a listener operation + * submitted or returned here will ever be invoked. Will always be invoked on the calling + * thread. + */ + protected TListenerOperation onExecuteOperation(@NonNull TListenerOperation operation) { + return operation; + } + + /** + * May be overridden by subclasses to handle listener operation failures. The default behavior + * is to further propagate any exceptions. Will always be invoked on the executor thread. + */ + protected void onOperationFailure(TListenerOperation operation, Exception exception) { + throw new AssertionError(exception); + } + + final void executeInternal(@NonNull TListenerOperation operation) { + executeSafely(mExecutor, () -> mListener, + onExecuteOperation(Objects.requireNonNull(operation)), this::onOperationFailure); } @Override public String toString() { - if (mRequest == null) { - return "[]"; - } else { - return mRequest.toString(); - } + return "[]"; } @Override diff --git a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java index 7b6154eb0d003..e57b5322de8db 100644 --- a/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/PendingIntentListenerRegistration.java @@ -21,6 +21,8 @@ import android.app.PendingIntent; import android.location.util.identity.CallerIdentity; import android.util.Log; +import com.android.internal.listeners.ListenerExecutor.ListenerOperation; + /** * A registration that works with PendingIntent keys, and registers a CancelListener to * automatically remove the registration if the PendingIntent is canceled. The key for this @@ -30,7 +32,8 @@ import android.util.Log; * @param listener type */ public abstract class PendingIntentListenerRegistration extends - RemoteListenerRegistration implements PendingIntent.CancelListener { + RemoteListenerRegistration> implements + PendingIntent.CancelListener { /** * Interface to allowed pending intent retrieval when keys are not themselves PendingIntents. @@ -42,9 +45,9 @@ public abstract class PendingIntentListenerRegistration ext PendingIntent getPendingIntent(); } - protected PendingIntentListenerRegistration(String tag, @Nullable TRequest request, + protected PendingIntentListenerRegistration(@Nullable TRequest request, CallerIdentity callerIdentity, TListener listener) { - super(tag, request, callerIdentity, listener); + super(request, callerIdentity, listener); } @Override @@ -69,10 +72,21 @@ public abstract class PendingIntentListenerRegistration ext */ protected void onPendingIntentListenerUnregister() {} + @Override + public void onOperationFailure(ListenerOperation operation, Exception e) { + if (e instanceof PendingIntent.CanceledException) { + Log.w(getOwner().getTag(), "registration " + this + " removed", e); + remove(); + } else { + super.onOperationFailure(operation, e); + } + } + @Override public void onCancelled(PendingIntent intent) { - if (Log.isLoggable(mTag, Log.DEBUG)) { - Log.d(mTag, "pending intent registration " + getIdentity() + " canceled"); + if (Log.isLoggable(getOwner().getTag(), Log.DEBUG)) { + Log.d(getOwner().getTag(), + "pending intent registration " + getIdentity() + " canceled"); } remove(); diff --git a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java index e4b0b190d34c3..242bf323f6cd9 100644 --- a/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/RemoteListenerRegistration.java @@ -24,6 +24,7 @@ import android.location.util.identity.CallerIdentity; import android.os.Process; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.listeners.ListenerExecutor.ListenerOperation; import com.android.server.FgThread; import java.util.Objects; @@ -35,11 +36,13 @@ import java.util.concurrent.Executor; * invocation should already be asynchronous. Listeners from the same process will be run on a * normal executor, since in-process listener invocation may be synchronous. * - * @param request type - * @param listener type + * @param request type + * @param listener type + * @param listener operation type */ -public abstract class RemoteListenerRegistration extends - RemovableListenerRegistration { +public abstract class RemoteListenerRegistration> extends + RemovableListenerRegistration { @VisibleForTesting public static final Executor IN_PROCESS_EXECUTOR = FgThread.getExecutor(); @@ -59,9 +62,9 @@ public abstract class RemoteListenerRegistration extends private final CallerIdentity mIdentity; - protected RemoteListenerRegistration(String tag, @Nullable TRequest request, - CallerIdentity identity, TListener listener) { - super(tag, chooseExecutor(identity), request, listener); + protected RemoteListenerRegistration(@Nullable TRequest request, CallerIdentity identity, + TListener listener) { + super(chooseExecutor(identity), request, listener); mIdentity = Objects.requireNonNull(identity); } diff --git a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java index 2383bece4e0a6..d3b5f6696167d 100644 --- a/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java +++ b/services/core/java/com/android/server/location/listeners/RemovableListenerRegistration.java @@ -17,7 +17,8 @@ package com.android.server.location.listeners; import android.annotation.Nullable; -import android.util.Log; + +import com.android.internal.listeners.ListenerExecutor.ListenerOperation; import java.util.Objects; import java.util.concurrent.Executor; @@ -26,20 +27,19 @@ import java.util.concurrent.Executor; * A listener registration that stores its own key, and thus can remove itself. By default it will * remove itself if any checked exception occurs on listener execution. * - * @param request type - * @param listener type + * @param request type + * @param listener type + * @param listener operation type */ -public abstract class RemovableListenerRegistration extends - ListenerRegistration { - - protected final String mTag; +public abstract class RemovableListenerRegistration> extends + RequestListenerRegistration { private volatile @Nullable Object mKey; - protected RemovableListenerRegistration(String tag, Executor executor, - @Nullable TRequest request, TListener listener) { + protected RemovableListenerRegistration(Executor executor, @Nullable TRequest request, + TListener listener) { super(executor, request, listener); - mTag = Objects.requireNonNull(tag); } /** @@ -47,7 +47,8 @@ public abstract class RemovableListenerRegistration extends * with. Often this is easiest to accomplish by defining registration subclasses as non-static * inner classes of the multiplexer they are to be used with. */ - protected abstract ListenerMultiplexer getOwner(); + protected abstract ListenerMultiplexer getOwner(); /** * Returns the key associated with this registration. May not be invoked before @@ -68,12 +69,6 @@ public abstract class RemovableListenerRegistration extends } } - @Override - public void onOperationFailure(ListenerOperation operation, Exception e) { - Log.w(mTag, "registration " + this + " removed due to unexpected exception", e); - remove(); - } - @Override protected final void onRegister(Object key) { mKey = Objects.requireNonNull(key); diff --git a/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java b/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java new file mode 100644 index 0000000000000..d97abae59dd38 --- /dev/null +++ b/services/core/java/com/android/server/location/listeners/RequestListenerRegistration.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.location.listeners; + +import com.android.internal.listeners.ListenerExecutor.ListenerOperation; + +import java.util.concurrent.Executor; + +/** + * A listener registration object which includes an associated request. + * + * @param request type + * @param listener type + * @param listener operation type + */ +public class RequestListenerRegistration> extends + ListenerRegistration { + + private final TRequest mRequest; + + protected RequestListenerRegistration(Executor executor, TRequest request, + TListener listener) { + super(executor, listener); + mRequest = request; + } + + /** + * Returns the request associated with this listener, or null if one wasn't supplied. + */ + public TRequest getRequest() { + return mRequest; + } + + @Override + public String toString() { + if (mRequest == null) { + return "[]"; + } else { + return mRequest.toString(); + } + } +} + diff --git a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java index 0b5a6998cd0d8..29d3f29ad7e1d 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/listeners/ListenerMultiplexerTest.java @@ -34,6 +34,7 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.listeners.ListenerExecutor.ListenerOperation; import com.android.server.location.listeners.ListenerMultiplexer.UpdateServiceLock; import org.junit.Before; @@ -318,7 +319,8 @@ public class ListenerMultiplexerTest { } private static class TestListenerRegistration extends - ListenerRegistration> { + RequestListenerRegistration, + ListenerOperation>> { boolean mActive = true; @@ -329,8 +331,10 @@ public class ListenerMultiplexerTest { } private static class TestMultiplexer extends - ListenerMultiplexer, Integer, - Consumer, TestListenerRegistration, Integer> { + ListenerMultiplexer, + Consumer, + ListenerOperation>, TestListenerRegistration, + Integer> { boolean mRegistered; int mMergedRequest; @@ -341,6 +345,11 @@ public class ListenerMultiplexerTest { mCallbacks = callbacks; } + @Override + public String getTag() { + return "TestMultiplexer"; + } + public void addListener(Integer request, Consumer consumer) { addRegistration(consumer, new TestListenerRegistration(request, consumer)); } @@ -369,7 +378,8 @@ public class ListenerMultiplexerTest { } @Override - protected boolean registerWithService(Integer mergedRequest) { + protected boolean registerWithService(Integer mergedRequest, + Collection registrations) { mRegistered = true; mMergedRequest = mergedRequest; return true; @@ -418,7 +428,8 @@ public class ListenerMultiplexerTest { } @Override - protected Integer mergeRequests(Collection testRegistrations) { + protected Integer mergeRegistrations( + Collection testRegistrations) { int max = Integer.MIN_VALUE; for (TestListenerRegistration registration : testRegistrations) { if (registration.getRequest() > max) {