Merge "4/n: Make CoexCoordinator multi-sensor-aware" into sc-dev am: c4815d0f2d

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15312046

Change-Id: I22fd2574840b20627c9eb6e923052ad8a60b70c0
This commit is contained in:
TreeHugger Robot
2021-07-19 19:37:54 +00:00
committed by Automerger Merge Worker
12 changed files with 150 additions and 24 deletions

View File

@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricService;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Handler; import android.os.Handler;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
@@ -48,8 +49,11 @@ import java.util.Locale;
/** /**
* A scheduler for biometric HAL operations. Maintains a queue of {@link BaseClientMonitor} * A scheduler for biometric HAL operations. Maintains a queue of {@link BaseClientMonitor}
* operations, without caring about its implementation details. Operations may perform one or more * operations, without caring about its implementation details. Operations may perform zero or more
* interactions with the HAL before finishing. * interactions with the HAL before finishing.
*
* We currently assume (and require) that each biometric sensor have its own instance of a
* {@link BiometricScheduler}. See {@link CoexCoordinator}.
*/ */
public class BiometricScheduler { public class BiometricScheduler {
@@ -57,6 +61,55 @@ public class BiometricScheduler {
// Number of recent operations to keep in our logs for dumpsys // Number of recent operations to keep in our logs for dumpsys
protected static final int LOG_NUM_RECENT_OPERATIONS = 50; protected static final int LOG_NUM_RECENT_OPERATIONS = 50;
/**
* Unknown sensor type. This should never be used, and is a sign that something is wrong during
* initialization.
*/
public static final int SENSOR_TYPE_UNKNOWN = 0;
/**
* Face authentication.
*/
public static final int SENSOR_TYPE_FACE = 1;
/**
* Any UDFPS type. See {@link FingerprintSensorPropertiesInternal#isAnyUdfpsType()}.
*/
public static final int SENSOR_TYPE_UDFPS = 2;
/**
* Any other fingerprint sensor. We can add additional definitions in the future when necessary.
*/
public static final int SENSOR_TYPE_FP_OTHER = 3;
@IntDef({SENSOR_TYPE_UNKNOWN, SENSOR_TYPE_FACE, SENSOR_TYPE_UDFPS, SENSOR_TYPE_FP_OTHER})
@Retention(RetentionPolicy.SOURCE)
public @interface SensorType {}
public static @SensorType int sensorTypeFromFingerprintProperties(
@NonNull FingerprintSensorPropertiesInternal props) {
if (props.isAnyUdfpsType()) {
return SENSOR_TYPE_UDFPS;
}
return SENSOR_TYPE_FP_OTHER;
}
public static String sensorTypeToString(@SensorType int sensorType) {
switch (sensorType) {
case SENSOR_TYPE_UNKNOWN:
return "Unknown";
case SENSOR_TYPE_FACE:
return "Face";
case SENSOR_TYPE_UDFPS:
return "Udfps";
case SENSOR_TYPE_FP_OTHER:
return "OtherFp";
default:
return "UnknownUnknown";
}
}
/** /**
* Contains all the necessary information for a HAL operation. * Contains all the necessary information for a HAL operation.
*/ */
@@ -207,6 +260,7 @@ public class BiometricScheduler {
} }
@NonNull protected final String mBiometricTag; @NonNull protected final String mBiometricTag;
private final @SensorType int mSensorType;
@Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@NonNull private final IBiometricService mBiometricService; @NonNull private final IBiometricService mBiometricService;
@NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper()); @NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -218,6 +272,7 @@ public class BiometricScheduler {
private int mTotalOperationsHandled; private int mTotalOperationsHandled;
private final int mRecentOperationsLimit; private final int mRecentOperationsLimit;
@NonNull private final List<Integer> mRecentOperations; @NonNull private final List<Integer> mRecentOperations;
@NonNull private final CoexCoordinator mCoexCoordinator;
// Internal callback, notified when an operation is complete. Notifies the requester // Internal callback, notified when an operation is complete. Notifies the requester
// that the operation is complete, before performing internal scheduler work (such as // that the operation is complete, before performing internal scheduler work (such as
@@ -226,6 +281,12 @@ public class BiometricScheduler {
@Override @Override
public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) {
Slog.d(getTag(), "[Started] " + clientMonitor); Slog.d(getTag(), "[Started] " + clientMonitor);
if (clientMonitor instanceof AuthenticationClient) {
mCoexCoordinator.addAuthenticationClient(mSensorType,
(AuthenticationClient<?>) clientMonitor);
}
if (mCurrentOperation.mClientCallback != null) { if (mCurrentOperation.mClientCallback != null) {
mCurrentOperation.mClientCallback.onClientStarted(clientMonitor); mCurrentOperation.mClientCallback.onClientStarted(clientMonitor);
} }
@@ -248,6 +309,11 @@ public class BiometricScheduler {
} }
Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success); Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success);
if (clientMonitor instanceof AuthenticationClient) {
mCoexCoordinator.removeAuthenticationClient(mSensorType,
(AuthenticationClient<?>) clientMonitor);
}
mCurrentOperation.mState = Operation.STATE_FINISHED; mCurrentOperation.mState = Operation.STATE_FINISHED;
if (mCurrentOperation.mClientCallback != null) { if (mCurrentOperation.mClientCallback != null) {
@@ -271,10 +337,12 @@ public class BiometricScheduler {
} }
@VisibleForTesting @VisibleForTesting
BiometricScheduler(@NonNull String tag, BiometricScheduler(@NonNull String tag, @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull IBiometricService biometricService, int recentOperationsLimit) { @NonNull IBiometricService biometricService, int recentOperationsLimit,
@NonNull CoexCoordinator coexCoordinator) {
mBiometricTag = tag; mBiometricTag = tag;
mSensorType = sensorType;
mInternalCallback = new InternalCallback(); mInternalCallback = new InternalCallback();
mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher; mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
mPendingOperations = new ArrayDeque<>(); mPendingOperations = new ArrayDeque<>();
@@ -282,6 +350,7 @@ public class BiometricScheduler {
mCrashStates = new ArrayDeque<>(); mCrashStates = new ArrayDeque<>();
mRecentOperationsLimit = recentOperationsLimit; mRecentOperationsLimit = recentOperationsLimit;
mRecentOperations = new ArrayList<>(); mRecentOperations = new ArrayList<>();
mCoexCoordinator = coexCoordinator;
} }
/** /**
@@ -290,10 +359,11 @@ public class BiometricScheduler {
* @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures
* (such as fingerprint swipe). * (such as fingerprint swipe).
*/ */
public BiometricScheduler(@NonNull String tag, public BiometricScheduler(@NonNull String tag, @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
this(tag, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS); ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS,
CoexCoordinator.getInstance());
} }
/** /**
@@ -645,6 +715,7 @@ public class BiometricScheduler {
public void dump(PrintWriter pw) { public void dump(PrintWriter pw) {
pw.println("Dump of BiometricScheduler " + getTag()); pw.println("Dump of BiometricScheduler " + getTag());
pw.println("Type: " + mSensorType);
pw.println("Current operation: " + mCurrentOperation); pw.println("Current operation: " + mCurrentOperation);
pw.println("Pending operations: " + mPendingOperations.size()); pw.println("Pending operations: " + mPendingOperations.size());
for (Operation operation : mPendingOperations) { for (Operation operation : mPendingOperations) {

View File

@@ -16,7 +16,13 @@
package com.android.server.biometrics.sensors; package com.android.server.biometrics.sensors;
import static com.android.server.biometrics.sensors.BiometricScheduler.sensorTypeToString;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.util.Slog;
import java.util.HashMap;
import java.util.Map;
/** /**
* Singleton that contains the core logic for determining if haptics and authentication callbacks * Singleton that contains the core logic for determining if haptics and authentication callbacks
@@ -27,6 +33,7 @@ import android.annotation.NonNull;
public class CoexCoordinator { public class CoexCoordinator {
private static final String TAG = "BiometricCoexCoordinator"; private static final String TAG = "BiometricCoexCoordinator";
private static final boolean DEBUG = true;
/** /**
* Callback interface notifying the owner of "results" from the CoexCoordinator's business * Callback interface notifying the owner of "results" from the CoexCoordinator's business
@@ -47,10 +54,6 @@ public class CoexCoordinator {
private static CoexCoordinator sInstance; private static CoexCoordinator sInstance;
private CoexCoordinator() {
// Singleton
}
@NonNull @NonNull
static CoexCoordinator getInstance() { static CoexCoordinator getInstance() {
if (sInstance == null) { if (sInstance == null) {
@@ -59,6 +62,43 @@ public class CoexCoordinator {
return sInstance; return sInstance;
} }
// SensorType to AuthenticationClient map
private final Map<Integer, AuthenticationClient<?>> mClientMap;
private CoexCoordinator() {
// Singleton
mClientMap = new HashMap<>();
}
public void addAuthenticationClient(@BiometricScheduler.SensorType int sensorType,
@NonNull AuthenticationClient<?> client) {
if (DEBUG) {
Slog.d(TAG, "addAuthenticationClient(" + sensorTypeToString(sensorType) + ")"
+ ", client: " + client);
}
if (mClientMap.containsKey(sensorType)) {
Slog.w(TAG, "Overwriting existing client: " + mClientMap.get(sensorType)
+ " with new client: " + client);
}
mClientMap.put(sensorType, client);
}
public void removeAuthenticationClient(@BiometricScheduler.SensorType int sensorType,
@NonNull AuthenticationClient<?> client) {
if (DEBUG) {
Slog.d(TAG, "removeAuthenticationClient(" + sensorTypeToString(sensorType) + ")"
+ ", client: " + client);
}
if (!mClientMap.containsKey(sensorType)) {
Slog.e(TAG, "sensorType: " + sensorType + " does not exist in map. Client: " + client);
return;
}
mClientMap.remove(sensorType);
}
public void onAuthenticationSucceeded(@NonNull AuthenticationClient<?> client, public void onAuthenticationSucceeded(@NonNull AuthenticationClient<?> client,
@NonNull Callback callback) { @NonNull Callback callback) {
if (client.isBiometricPrompt()) { if (client.isBiometricPrompt()) {

View File

@@ -83,24 +83,26 @@ public class UserAwareBiometricScheduler extends BiometricScheduler {
} }
@VisibleForTesting @VisibleForTesting
UserAwareBiometricScheduler(@NonNull String tag, UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull IBiometricService biometricService, @NonNull IBiometricService biometricService,
@NonNull CurrentUserRetriever currentUserRetriever, @NonNull CurrentUserRetriever currentUserRetriever,
@NonNull UserSwitchCallback userSwitchCallback) { @NonNull UserSwitchCallback userSwitchCallback,
super(tag, gestureAvailabilityDispatcher, biometricService, LOG_NUM_RECENT_OPERATIONS); @NonNull CoexCoordinator coexCoordinator) {
super(tag, sensorType, gestureAvailabilityDispatcher, biometricService,
LOG_NUM_RECENT_OPERATIONS, coexCoordinator);
mCurrentUserRetriever = currentUserRetriever; mCurrentUserRetriever = currentUserRetriever;
mUserSwitchCallback = userSwitchCallback; mUserSwitchCallback = userSwitchCallback;
} }
public UserAwareBiometricScheduler(@NonNull String tag, public UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher,
@NonNull CurrentUserRetriever currentUserRetriever, @NonNull CurrentUserRetriever currentUserRetriever,
@NonNull UserSwitchCallback userSwitchCallback) { @NonNull UserSwitchCallback userSwitchCallback) {
this(tag, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE)), currentUserRetriever, ServiceManager.getService(Context.BIOMETRIC_SERVICE)), currentUserRetriever,
userSwitchCallback); userSwitchCallback, CoexCoordinator.getInstance());
} }
@Override @Override

View File

@@ -494,7 +494,8 @@ public class Sensor {
mToken = new Binder(); mToken = new Binder();
mHandler = handler; mHandler = handler;
mSensorProperties = sensorProperties; mSensorProperties = sensorProperties;
mScheduler = new UserAwareBiometricScheduler(tag, null /* gestureAvailabilityDispatcher */, mScheduler = new UserAwareBiometricScheduler(tag, BiometricScheduler.SENSOR_TYPE_FACE,
null /* gestureAvailabilityDispatcher */,
() -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL, () -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
new UserAwareBiometricScheduler.UserSwitchCallback() { new UserAwareBiometricScheduler.UserSwitchCallback() {
@NonNull @NonNull

View File

@@ -355,7 +355,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
public Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps, public Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) { @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
this(context, sensorProps, lockoutResetDispatcher, this(context, sensorProps, lockoutResetDispatcher,
new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */)); new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE,
null /* gestureAvailabilityTracker */));
} }
@Override @Override

View File

@@ -449,7 +449,9 @@ class Sensor {
mHandler = handler; mHandler = handler;
mSensorProperties = sensorProperties; mSensorProperties = sensorProperties;
mLockoutCache = new LockoutCache(); mLockoutCache = new LockoutCache();
mScheduler = new UserAwareBiometricScheduler(tag, gestureAvailabilityDispatcher, mScheduler = new UserAwareBiometricScheduler(tag,
BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties),
gestureAvailabilityDispatcher,
() -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL, () -> mCurrentSession != null ? mCurrentSession.mUserId : UserHandle.USER_NULL,
new UserAwareBiometricScheduler.UserSwitchCallback() { new UserAwareBiometricScheduler.UserSwitchCallback() {
@NonNull @NonNull

View File

@@ -352,7 +352,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
@NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
final Handler handler = new Handler(Looper.getMainLooper()); final Handler handler = new Handler(Looper.getMainLooper());
final BiometricScheduler scheduler = final BiometricScheduler scheduler =
new BiometricScheduler(TAG, gestureAvailabilityDispatcher); new BiometricScheduler(TAG,
BiometricScheduler.sensorTypeFromFingerprintProperties(sensorProps),
gestureAvailabilityDispatcher);
final HalResultController controller = new HalResultController(sensorProps.sensorId, final HalResultController controller = new HalResultController(sensorProps.sensorId,
context, handler, context, handler,
scheduler); scheduler);

View File

@@ -138,7 +138,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage
TestableBiometricScheduler(@NonNull String tag, TestableBiometricScheduler(@NonNull String tag,
@Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
super(tag, gestureAvailabilityDispatcher); super(tag, BiometricScheduler.SENSOR_TYPE_FP_OTHER, gestureAvailabilityDispatcher);
mInternalCallback = new TestableInternalCallback(); mInternalCallback = new TestableInternalCallback();
} }

View File

@@ -75,8 +75,9 @@ public class BiometricSchedulerTest {
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mToken = new Binder(); mToken = new Binder();
mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */, mScheduler = new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_UNKNOWN,
mBiometricService, LOG_NUM_RECENT_OPERATIONS); null /* gestureAvailabilityTracker */, mBiometricService, LOG_NUM_RECENT_OPERATIONS,
CoexCoordinator.getInstance());
} }
@Test @Test

View File

@@ -74,6 +74,7 @@ public class UserAwareBiometricSchedulerTest {
mUserStoppedCallback = new TestUserStoppedCallback(); mUserStoppedCallback = new TestUserStoppedCallback();
mScheduler = new UserAwareBiometricScheduler(TAG, mScheduler = new UserAwareBiometricScheduler(TAG,
BiometricScheduler.SENSOR_TYPE_UNKNOWN,
null /* gestureAvailabilityDispatcher */, null /* gestureAvailabilityDispatcher */,
mBiometricService, mBiometricService,
() -> mCurrentUserId, () -> mCurrentUserId,
@@ -92,7 +93,8 @@ public class UserAwareBiometricSchedulerTest {
return new TestStartUserClient(mContext, Object::new, mToken, newUserId, return new TestStartUserClient(mContext, Object::new, mToken, newUserId,
TEST_SENSOR_ID, mUserStartedCallback, mStartOperationsFinish); TEST_SENSOR_ID, mUserStartedCallback, mStartOperationsFinish);
} }
}); },
CoexCoordinator.getInstance());
} }
@Test @Test

View File

@@ -32,6 +32,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.LockoutTracker;
@@ -78,6 +79,7 @@ public class SensorTest {
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService); when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
mScheduler = new UserAwareBiometricScheduler(TAG, mScheduler = new UserAwareBiometricScheduler(TAG,
BiometricScheduler.SENSOR_TYPE_FACE,
null /* gestureAvailabilityDispatcher */, null /* gestureAvailabilityDispatcher */,
() -> USER_ID, () -> USER_ID,
mUserSwitchCallback); mUserSwitchCallback);

View File

@@ -32,6 +32,7 @@ import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.LockoutTracker;
@@ -78,6 +79,7 @@ public class SensorTest {
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService); when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
mScheduler = new UserAwareBiometricScheduler(TAG, mScheduler = new UserAwareBiometricScheduler(TAG,
BiometricScheduler.SENSOR_TYPE_FP_OTHER,
null /* gestureAvailabilityDispatcher */, null /* gestureAvailabilityDispatcher */,
() -> USER_ID, () -> USER_ID,
mUserSwitchCallback); mUserSwitchCallback);