Loading services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +14 −2 Original line number Diff line number Diff line Loading @@ -82,7 +82,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> private long mStartTimeMs; protected boolean mAuthAttempted; private boolean mAuthAttempted; // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update // the state. We should think of a way to improve this in the future. Loading @@ -98,6 +98,12 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> */ protected abstract void handleLifecycleAfterAuth(boolean authenticated); /** * @return true if a user was detected (i.e. face was found, fingerprint sensor was touched. * etc) */ public abstract boolean wasUserDetected(); public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, Loading Loading @@ -381,9 +387,11 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> } @Override public void onError(int errorCode, int vendorCode) { public void onError(@BiometricConstants.Errors int errorCode, int vendorCode) { super.onError(errorCode, vendorCode); mState = STATE_STOPPED; CoexCoordinator.getInstance().onAuthenticationError(this, errorCode, this::vibrateError); } /** Loading Loading @@ -445,4 +453,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> public boolean interruptsPrecedingClients() { return true; } public boolean wasAuthAttempted() { return mAuthAttempted; } } services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java +67 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.server.biometrics.sensors.BiometricScheduler.sensorTyp import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.biometrics.BiometricConstants; import android.os.Handler; import android.os.Looper; import android.util.Slog; Loading Loading @@ -54,7 +55,7 @@ public class CoexCoordinator { /** * Callback interface notifying the owner of "results" from the CoexCoordinator's business * logic. * logic for accept and reject. */ interface Callback { /** Loading @@ -80,6 +81,17 @@ public class CoexCoordinator { void sendAuthenticationCanceled(); } /** * Callback interface notifying the owner of "results" from the CoexCoordinator's business * logic for errors. */ interface ErrorCallback { /** * Requests the owner to initiate a vibration for this event. */ void sendHapticFeedback(); } private static CoexCoordinator sInstance; @VisibleForTesting Loading Loading @@ -203,6 +215,9 @@ public class CoexCoordinator { mClientMap.remove(sensorType); } /** * Notify the coordinator that authentication succeeded (accepted) */ public void onAuthenticationSucceeded(long currentTimeMillis, @NonNull AuthenticationClient<?> client, @NonNull Callback callback) { Loading Loading @@ -273,6 +288,9 @@ public class CoexCoordinator { } } /** * Notify the coordinator that a rejection has occurred. */ public void onAuthenticationRejected(long currentTimeMillis, @NonNull AuthenticationClient<?> client, @LockoutTracker.LockoutMode int lockoutMode, Loading Loading @@ -357,6 +375,54 @@ public class CoexCoordinator { } } /** * Notify the coordinator that an error has occurred. */ public void onAuthenticationError(@NonNull AuthenticationClient<?> client, @BiometricConstants.Errors int error, @NonNull ErrorCallback callback) { // Figure out non-coex state final boolean shouldUsuallyVibrate; if (isCurrentFaceAuth(client)) { final boolean notDetectedOnKeyguard = client.isKeyguard() && !client.wasUserDetected(); final boolean authAttempted = client.wasAuthAttempted(); switch (error) { case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT: case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: shouldUsuallyVibrate = authAttempted && !notDetectedOnKeyguard; break; default: shouldUsuallyVibrate = false; break; } } else { shouldUsuallyVibrate = false; } // Figure out coex state final boolean keyguardAdvancedLogic = mAdvancedLogicEnabled && client.isKeyguard(); final boolean hapticSuppressedByCoex; if (keyguardAdvancedLogic) { if (isSingleAuthOnly(client)) { hapticSuppressedByCoex = false; } else { hapticSuppressedByCoex = isCurrentFaceAuth(client) && !client.isKeyguardBypassEnabled(); } } else { hapticSuppressedByCoex = false; } // Combine and send feedback if appropriate Slog.d(TAG, "shouldUsuallyVibrate: " + shouldUsuallyVibrate + ", hapticSuppressedByCoex: " + hapticSuppressedByCoex); if (shouldUsuallyVibrate && !hapticSuppressedByCoex) { callback.sendHapticFeedback(); } } @Nullable private SuccessfulAuth popSuccessfulFaceAuthIfExists(long currentTimeMillis) { for (SuccessfulAuth auth : mSuccessfulAuths) { Loading services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +5 −21 Original line number Diff line number Diff line Loading @@ -127,7 +127,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements } } private boolean wasUserDetected() { @Override public boolean wasUserDetected() { // Do not provide haptic feedback if the user was not detected, and an error (usually // ERROR_TIMEOUT) is received. return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED Loading Loading @@ -160,7 +161,7 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements } @Override public void onError(int error, int vendorCode) { public void onError(@BiometricConstants.Errors int error, int vendorCode) { mUsageStats.addEvent(new UsageStats.AuthenticationEvent( getStartTimeMs(), System.currentTimeMillis() - getStartTimeMs() /* latency */, Loading @@ -169,25 +170,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements vendorCode, getTargetUserId())); switch (error) { case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT: if (!wasUserDetected() && !isBiometricPrompt()) { // No vibration if user was not detected on keyguard break; } case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: if (mAuthAttempted) { // Only vibrate if auth was attempted. If the user was already locked out prior // to starting authentication, do not vibrate. vibrateError(); } break; case BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL: if (error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL) { BiometricNotificationUtils.showReEnrollmentNotification(getContext()); break; default: break; } super.onError(error, vendorCode); Loading services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +3 −20 Original line number Diff line number Diff line Loading @@ -115,7 +115,8 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { } } private boolean wasUserDetected() { @Override public boolean wasUserDetected() { // Do not provide haptic feedback if the user was not detected, and an error (usually // ERROR_TIMEOUT) is received. return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED Loading Loading @@ -147,7 +148,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { } @Override public void onError(int error, int vendorCode) { public void onError(@BiometricConstants.Errors int error, int vendorCode) { mUsageStats.addEvent(new UsageStats.AuthenticationEvent( getStartTimeMs(), System.currentTimeMillis() - getStartTimeMs() /* latency */, Loading @@ -156,24 +157,6 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { vendorCode, getTargetUserId())); switch (error) { case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT: if (!wasUserDetected() && !isBiometricPrompt()) { // No vibration if user was not detected on keyguard break; } case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: if (mAuthAttempted) { // Only vibrate if auth was attempted. If the user was already locked out prior // to starting authentication, do not vibrate. vibrateError(); } break; default: break; } super.onError(error, vendorCode); } Loading services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +6 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp } } @Override public boolean wasUserDetected() { // TODO: Update if it needs to be used for fingerprint, i.e. success/reject, error_timeout return false; } @Override public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token) { Loading Loading
services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +14 −2 Original line number Diff line number Diff line Loading @@ -82,7 +82,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> private long mStartTimeMs; protected boolean mAuthAttempted; private boolean mAuthAttempted; // TODO: This is currently hard to maintain, as each AuthenticationClient subclass must update // the state. We should think of a way to improve this in the future. Loading @@ -98,6 +98,12 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> */ protected abstract void handleLifecycleAfterAuth(boolean authenticated); /** * @return true if a user was detected (i.e. face was found, fingerprint sensor was touched. * etc) */ public abstract boolean wasUserDetected(); public AuthenticationClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, Loading Loading @@ -381,9 +387,11 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> } @Override public void onError(int errorCode, int vendorCode) { public void onError(@BiometricConstants.Errors int errorCode, int vendorCode) { super.onError(errorCode, vendorCode); mState = STATE_STOPPED; CoexCoordinator.getInstance().onAuthenticationError(this, errorCode, this::vibrateError); } /** Loading Loading @@ -445,4 +453,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> public boolean interruptsPrecedingClients() { return true; } public boolean wasAuthAttempted() { return mAuthAttempted; } }
services/core/java/com/android/server/biometrics/sensors/CoexCoordinator.java +67 −1 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.server.biometrics.sensors.BiometricScheduler.sensorTyp import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.biometrics.BiometricConstants; import android.os.Handler; import android.os.Looper; import android.util.Slog; Loading Loading @@ -54,7 +55,7 @@ public class CoexCoordinator { /** * Callback interface notifying the owner of "results" from the CoexCoordinator's business * logic. * logic for accept and reject. */ interface Callback { /** Loading @@ -80,6 +81,17 @@ public class CoexCoordinator { void sendAuthenticationCanceled(); } /** * Callback interface notifying the owner of "results" from the CoexCoordinator's business * logic for errors. */ interface ErrorCallback { /** * Requests the owner to initiate a vibration for this event. */ void sendHapticFeedback(); } private static CoexCoordinator sInstance; @VisibleForTesting Loading Loading @@ -203,6 +215,9 @@ public class CoexCoordinator { mClientMap.remove(sensorType); } /** * Notify the coordinator that authentication succeeded (accepted) */ public void onAuthenticationSucceeded(long currentTimeMillis, @NonNull AuthenticationClient<?> client, @NonNull Callback callback) { Loading Loading @@ -273,6 +288,9 @@ public class CoexCoordinator { } } /** * Notify the coordinator that a rejection has occurred. */ public void onAuthenticationRejected(long currentTimeMillis, @NonNull AuthenticationClient<?> client, @LockoutTracker.LockoutMode int lockoutMode, Loading Loading @@ -357,6 +375,54 @@ public class CoexCoordinator { } } /** * Notify the coordinator that an error has occurred. */ public void onAuthenticationError(@NonNull AuthenticationClient<?> client, @BiometricConstants.Errors int error, @NonNull ErrorCallback callback) { // Figure out non-coex state final boolean shouldUsuallyVibrate; if (isCurrentFaceAuth(client)) { final boolean notDetectedOnKeyguard = client.isKeyguard() && !client.wasUserDetected(); final boolean authAttempted = client.wasAuthAttempted(); switch (error) { case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT: case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: shouldUsuallyVibrate = authAttempted && !notDetectedOnKeyguard; break; default: shouldUsuallyVibrate = false; break; } } else { shouldUsuallyVibrate = false; } // Figure out coex state final boolean keyguardAdvancedLogic = mAdvancedLogicEnabled && client.isKeyguard(); final boolean hapticSuppressedByCoex; if (keyguardAdvancedLogic) { if (isSingleAuthOnly(client)) { hapticSuppressedByCoex = false; } else { hapticSuppressedByCoex = isCurrentFaceAuth(client) && !client.isKeyguardBypassEnabled(); } } else { hapticSuppressedByCoex = false; } // Combine and send feedback if appropriate Slog.d(TAG, "shouldUsuallyVibrate: " + shouldUsuallyVibrate + ", hapticSuppressedByCoex: " + hapticSuppressedByCoex); if (shouldUsuallyVibrate && !hapticSuppressedByCoex) { callback.sendHapticFeedback(); } } @Nullable private SuccessfulAuth popSuccessfulFaceAuthIfExists(long currentTimeMillis) { for (SuccessfulAuth auth : mSuccessfulAuths) { Loading
services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +5 −21 Original line number Diff line number Diff line Loading @@ -127,7 +127,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements } } private boolean wasUserDetected() { @Override public boolean wasUserDetected() { // Do not provide haptic feedback if the user was not detected, and an error (usually // ERROR_TIMEOUT) is received. return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED Loading Loading @@ -160,7 +161,7 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements } @Override public void onError(int error, int vendorCode) { public void onError(@BiometricConstants.Errors int error, int vendorCode) { mUsageStats.addEvent(new UsageStats.AuthenticationEvent( getStartTimeMs(), System.currentTimeMillis() - getStartTimeMs() /* latency */, Loading @@ -169,25 +170,8 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements vendorCode, getTargetUserId())); switch (error) { case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT: if (!wasUserDetected() && !isBiometricPrompt()) { // No vibration if user was not detected on keyguard break; } case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: if (mAuthAttempted) { // Only vibrate if auth was attempted. If the user was already locked out prior // to starting authentication, do not vibrate. vibrateError(); } break; case BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL: if (error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL) { BiometricNotificationUtils.showReEnrollmentNotification(getContext()); break; default: break; } super.onError(error, vendorCode); Loading
services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +3 −20 Original line number Diff line number Diff line Loading @@ -115,7 +115,8 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { } } private boolean wasUserDetected() { @Override public boolean wasUserDetected() { // Do not provide haptic feedback if the user was not detected, and an error (usually // ERROR_TIMEOUT) is received. return mLastAcquire != FaceManager.FACE_ACQUIRED_NOT_DETECTED Loading Loading @@ -147,7 +148,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { } @Override public void onError(int error, int vendorCode) { public void onError(@BiometricConstants.Errors int error, int vendorCode) { mUsageStats.addEvent(new UsageStats.AuthenticationEvent( getStartTimeMs(), System.currentTimeMillis() - getStartTimeMs() /* latency */, Loading @@ -156,24 +157,6 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { vendorCode, getTargetUserId())); switch (error) { case BiometricConstants.BIOMETRIC_ERROR_TIMEOUT: if (!wasUserDetected() && !isBiometricPrompt()) { // No vibration if user was not detected on keyguard break; } case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT: case BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT: if (mAuthAttempted) { // Only vibrate if auth was attempted. If the user was already locked out prior // to starting authentication, do not vibrate. vibrateError(); } break; default: break; } super.onError(error, vendorCode); } Loading
services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +6 −0 Original line number Diff line number Diff line Loading @@ -105,6 +105,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp } } @Override public boolean wasUserDetected() { // TODO: Update if it needs to be used for fingerprint, i.e. success/reject, error_timeout return false; } @Override public void onAuthenticated(BiometricAuthenticator.Identifier identifier, boolean authenticated, ArrayList<Byte> token) { Loading