Loading services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +77 −6 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.IBiometricService; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Handler; import android.os.IBinder; import android.os.Looper; Loading @@ -48,8 +49,11 @@ import java.util.Locale; /** * 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. * * We currently assume (and require) that each biometric sensor have its own instance of a * {@link BiometricScheduler}. See {@link CoexCoordinator}. */ public class BiometricScheduler { Loading @@ -57,6 +61,55 @@ public class BiometricScheduler { // Number of recent operations to keep in our logs for dumpsys 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. */ Loading Loading @@ -207,6 +260,7 @@ public class BiometricScheduler { } @NonNull protected final String mBiometricTag; private final @SensorType int mSensorType; @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; @NonNull private final IBiometricService mBiometricService; @NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper()); Loading @@ -218,6 +272,7 @@ public class BiometricScheduler { private int mTotalOperationsHandled; private final int mRecentOperationsLimit; @NonNull private final List<Integer> mRecentOperations; @NonNull private final CoexCoordinator mCoexCoordinator; // Internal callback, notified when an operation is complete. Notifies the requester // that the operation is complete, before performing internal scheduler work (such as Loading @@ -226,6 +281,12 @@ public class BiometricScheduler { @Override public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { Slog.d(getTag(), "[Started] " + clientMonitor); if (clientMonitor instanceof AuthenticationClient) { mCoexCoordinator.addAuthenticationClient(mSensorType, (AuthenticationClient<?>) clientMonitor); } if (mCurrentOperation.mClientCallback != null) { mCurrentOperation.mClientCallback.onClientStarted(clientMonitor); } Loading @@ -248,6 +309,11 @@ public class BiometricScheduler { } Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success); if (clientMonitor instanceof AuthenticationClient) { mCoexCoordinator.removeAuthenticationClient(mSensorType, (AuthenticationClient<?>) clientMonitor); } mCurrentOperation.mState = Operation.STATE_FINISHED; if (mCurrentOperation.mClientCallback != null) { Loading @@ -271,10 +337,12 @@ public class BiometricScheduler { } @VisibleForTesting BiometricScheduler(@NonNull String tag, BiometricScheduler(@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull IBiometricService biometricService, int recentOperationsLimit) { @NonNull IBiometricService biometricService, int recentOperationsLimit, @NonNull CoexCoordinator coexCoordinator) { mBiometricTag = tag; mSensorType = sensorType; mInternalCallback = new InternalCallback(); mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher; mPendingOperations = new ArrayDeque<>(); Loading @@ -282,6 +350,7 @@ public class BiometricScheduler { mCrashStates = new ArrayDeque<>(); mRecentOperationsLimit = recentOperationsLimit; mRecentOperations = new ArrayList<>(); mCoexCoordinator = coexCoordinator; } /** Loading @@ -290,10 +359,11 @@ public class BiometricScheduler { * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures * (such as fingerprint swipe). */ public BiometricScheduler(@NonNull String tag, public BiometricScheduler(@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { this(tag, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS); this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS, CoexCoordinator.getInstance()); } /** Loading Loading @@ -645,6 +715,7 @@ public class BiometricScheduler { public void dump(PrintWriter pw) { pw.println("Dump of BiometricScheduler " + getTag()); pw.println("Type: " + mSensorType); pw.println("Current operation: " + mCurrentOperation); pw.println("Pending operations: " + mPendingOperations.size()); for (Operation operation : mPendingOperations) { Loading services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java +44 −4 Original line number Diff line number Diff line Loading @@ -16,7 +16,13 @@ package com.android.server.biometrics.sensors; import static com.android.server.biometrics.sensors.BiometricScheduler.sensorTypeToString; 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 Loading @@ -27,6 +33,7 @@ import android.annotation.NonNull; public class CoexCoordinator { private static final String TAG = "BiometricCoexCoordinator"; private static final boolean DEBUG = true; /** * Callback interface notifying the owner of "results" from the CoexCoordinator's business Loading @@ -47,10 +54,6 @@ public class CoexCoordinator { private static CoexCoordinator sInstance; private CoexCoordinator() { // Singleton } @NonNull static CoexCoordinator getInstance() { if (sInstance == null) { Loading @@ -59,6 +62,43 @@ public class CoexCoordinator { 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, @NonNull Callback callback) { if (client.isBiometricPrompt()) { Loading services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java +8 −6 Original line number Diff line number Diff line Loading @@ -83,24 +83,26 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { } @VisibleForTesting UserAwareBiometricScheduler(@NonNull String tag, UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull IBiometricService biometricService, @NonNull CurrentUserRetriever currentUserRetriever, @NonNull UserSwitchCallback userSwitchCallback) { super(tag, gestureAvailabilityDispatcher, biometricService, LOG_NUM_RECENT_OPERATIONS); @NonNull UserSwitchCallback userSwitchCallback, @NonNull CoexCoordinator coexCoordinator) { super(tag, sensorType, gestureAvailabilityDispatcher, biometricService, LOG_NUM_RECENT_OPERATIONS, coexCoordinator); mCurrentUserRetriever = currentUserRetriever; mUserSwitchCallback = userSwitchCallback; } public UserAwareBiometricScheduler(@NonNull String tag, public UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull CurrentUserRetriever currentUserRetriever, @NonNull UserSwitchCallback userSwitchCallback) { this(tag, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), currentUserRetriever, userSwitchCallback); userSwitchCallback, CoexCoordinator.getInstance()); } @Override Loading services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +2 −1 Original line number Diff line number Diff line Loading @@ -494,7 +494,8 @@ public class Sensor { mToken = new Binder(); mHandler = handler; 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, new UserAwareBiometricScheduler.UserSwitchCallback() { @NonNull Loading services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +2 −1 Original line number Diff line number Diff line Loading @@ -355,7 +355,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { public Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher) { this(context, sensorProps, lockoutResetDispatcher, new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */)); new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityTracker */)); } @Override Loading Loading
services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +77 −6 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.IBiometricService; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Handler; import android.os.IBinder; import android.os.Looper; Loading @@ -48,8 +49,11 @@ import java.util.Locale; /** * 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. * * We currently assume (and require) that each biometric sensor have its own instance of a * {@link BiometricScheduler}. See {@link CoexCoordinator}. */ public class BiometricScheduler { Loading @@ -57,6 +61,55 @@ public class BiometricScheduler { // Number of recent operations to keep in our logs for dumpsys 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. */ Loading Loading @@ -207,6 +260,7 @@ public class BiometricScheduler { } @NonNull protected final String mBiometricTag; private final @SensorType int mSensorType; @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; @NonNull private final IBiometricService mBiometricService; @NonNull protected final Handler mHandler = new Handler(Looper.getMainLooper()); Loading @@ -218,6 +272,7 @@ public class BiometricScheduler { private int mTotalOperationsHandled; private final int mRecentOperationsLimit; @NonNull private final List<Integer> mRecentOperations; @NonNull private final CoexCoordinator mCoexCoordinator; // Internal callback, notified when an operation is complete. Notifies the requester // that the operation is complete, before performing internal scheduler work (such as Loading @@ -226,6 +281,12 @@ public class BiometricScheduler { @Override public void onClientStarted(@NonNull BaseClientMonitor clientMonitor) { Slog.d(getTag(), "[Started] " + clientMonitor); if (clientMonitor instanceof AuthenticationClient) { mCoexCoordinator.addAuthenticationClient(mSensorType, (AuthenticationClient<?>) clientMonitor); } if (mCurrentOperation.mClientCallback != null) { mCurrentOperation.mClientCallback.onClientStarted(clientMonitor); } Loading @@ -248,6 +309,11 @@ public class BiometricScheduler { } Slog.d(getTag(), "[Finishing] " + clientMonitor + ", success: " + success); if (clientMonitor instanceof AuthenticationClient) { mCoexCoordinator.removeAuthenticationClient(mSensorType, (AuthenticationClient<?>) clientMonitor); } mCurrentOperation.mState = Operation.STATE_FINISHED; if (mCurrentOperation.mClientCallback != null) { Loading @@ -271,10 +337,12 @@ public class BiometricScheduler { } @VisibleForTesting BiometricScheduler(@NonNull String tag, BiometricScheduler(@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull IBiometricService biometricService, int recentOperationsLimit) { @NonNull IBiometricService biometricService, int recentOperationsLimit, @NonNull CoexCoordinator coexCoordinator) { mBiometricTag = tag; mSensorType = sensorType; mInternalCallback = new InternalCallback(); mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher; mPendingOperations = new ArrayDeque<>(); Loading @@ -282,6 +350,7 @@ public class BiometricScheduler { mCrashStates = new ArrayDeque<>(); mRecentOperationsLimit = recentOperationsLimit; mRecentOperations = new ArrayList<>(); mCoexCoordinator = coexCoordinator; } /** Loading @@ -290,10 +359,11 @@ public class BiometricScheduler { * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures * (such as fingerprint swipe). */ public BiometricScheduler(@NonNull String tag, public BiometricScheduler(@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { this(tag, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS); this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), LOG_NUM_RECENT_OPERATIONS, CoexCoordinator.getInstance()); } /** Loading Loading @@ -645,6 +715,7 @@ public class BiometricScheduler { public void dump(PrintWriter pw) { pw.println("Dump of BiometricScheduler " + getTag()); pw.println("Type: " + mSensorType); pw.println("Current operation: " + mCurrentOperation); pw.println("Pending operations: " + mPendingOperations.size()); for (Operation operation : mPendingOperations) { Loading
services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java +44 −4 Original line number Diff line number Diff line Loading @@ -16,7 +16,13 @@ package com.android.server.biometrics.sensors; import static com.android.server.biometrics.sensors.BiometricScheduler.sensorTypeToString; 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 Loading @@ -27,6 +33,7 @@ import android.annotation.NonNull; public class CoexCoordinator { private static final String TAG = "BiometricCoexCoordinator"; private static final boolean DEBUG = true; /** * Callback interface notifying the owner of "results" from the CoexCoordinator's business Loading @@ -47,10 +54,6 @@ public class CoexCoordinator { private static CoexCoordinator sInstance; private CoexCoordinator() { // Singleton } @NonNull static CoexCoordinator getInstance() { if (sInstance == null) { Loading @@ -59,6 +62,43 @@ public class CoexCoordinator { 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, @NonNull Callback callback) { if (client.isBiometricPrompt()) { Loading
services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java +8 −6 Original line number Diff line number Diff line Loading @@ -83,24 +83,26 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { } @VisibleForTesting UserAwareBiometricScheduler(@NonNull String tag, UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull IBiometricService biometricService, @NonNull CurrentUserRetriever currentUserRetriever, @NonNull UserSwitchCallback userSwitchCallback) { super(tag, gestureAvailabilityDispatcher, biometricService, LOG_NUM_RECENT_OPERATIONS); @NonNull UserSwitchCallback userSwitchCallback, @NonNull CoexCoordinator coexCoordinator) { super(tag, sensorType, gestureAvailabilityDispatcher, biometricService, LOG_NUM_RECENT_OPERATIONS, coexCoordinator); mCurrentUserRetriever = currentUserRetriever; mUserSwitchCallback = userSwitchCallback; } public UserAwareBiometricScheduler(@NonNull String tag, public UserAwareBiometricScheduler(@NonNull String tag, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull CurrentUserRetriever currentUserRetriever, @NonNull UserSwitchCallback userSwitchCallback) { this(tag, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( this(tag, sensorType, gestureAvailabilityDispatcher, IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), currentUserRetriever, userSwitchCallback); userSwitchCallback, CoexCoordinator.getInstance()); } @Override Loading
services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +2 −1 Original line number Diff line number Diff line Loading @@ -494,7 +494,8 @@ public class Sensor { mToken = new Binder(); mHandler = handler; 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, new UserAwareBiometricScheduler.UserSwitchCallback() { @NonNull Loading
services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +2 −1 Original line number Diff line number Diff line Loading @@ -355,7 +355,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { public Face10(@NonNull Context context, @NonNull FaceSensorPropertiesInternal sensorProps, @NonNull LockoutResetDispatcher lockoutResetDispatcher) { this(context, sensorProps, lockoutResetDispatcher, new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */)); new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityTracker */)); } @Override Loading