diff options
5 files changed, 157 insertions, 23 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java index fb160f2a2f00..69ce78ce30a8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java @@ -109,6 +109,7 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom int ACTION_ERROR = 5; int ACTION_USE_DEVICE_CREDENTIAL = 6; int ACTION_START_DELAYED_FINGERPRINT_SENSOR = 7; + int ACTION_AUTHENTICATED_AND_CONFIRMED = 8; /** * When an action has occurred. The caller will only invoke this when the callback should @@ -509,7 +510,8 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom } public void updateState(@BiometricState int newState) { - Log.v(TAG, "newState: " + newState); + Log.d(TAG, "newState: " + newState); + mIconController.updateState(mState, newState); switch (newState) { @@ -533,8 +535,14 @@ public abstract class AuthBiometricView extends LinearLayout implements AuthBiom } announceForAccessibility(getResources() .getString(R.string.biometric_dialog_authenticated)); - mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED), - getDelayAfterAuthenticatedDurationMs()); + if (mState == STATE_PENDING_CONFIRMATION) { + mHandler.postDelayed(() -> mCallback.onAction( + Callback.ACTION_AUTHENTICATED_AND_CONFIRMED), + getDelayAfterAuthenticatedDurationMs()); + } else { + mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED), + getDelayAfterAuthenticatedDurationMs()); + } break; case STATE_PENDING_CONFIRMATION: diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 49ac26411d3e..43a3b9958ee5 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -213,6 +213,9 @@ public class AuthContainerView extends LinearLayout case AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR: mConfig.mCallback.onStartFingerprintNow(getRequestId()); break; + case AuthBiometricView.Callback.ACTION_AUTHENTICATED_AND_CONFIRMED: + animateAway(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE); + break; default: Log.e(TAG, "Unhandled action: " + action); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt index 2d1e8a830fc9..a93af7dd7450 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt @@ -99,7 +99,7 @@ class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() { waitForIdleSync() assertThat(biometricView.isAuthenticated).isTrue() - verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED) + verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED_AND_CONFIRMED) } @Test diff --git a/services/core/java/com/android/server/biometrics/AuthSession.java b/services/core/java/com/android/server/biometrics/AuthSession.java index 1989bc77583a..8ef2a1bd26c2 100644 --- a/services/core/java/com/android/server/biometrics/AuthSession.java +++ b/services/core/java/com/android/server/biometrics/AuthSession.java @@ -76,7 +76,7 @@ import java.util.function.Function; */ public final class AuthSession implements IBinder.DeathRecipient { private static final String TAG = "BiometricService/AuthSession"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; /* * Defined in biometrics.proto @@ -118,6 +118,7 @@ public final class AuthSession implements IBinder.DeathRecipient { @VisibleForTesting final IBinder mToken; // Info to be shown on BiometricDialog when all cookies are returned. @VisibleForTesting final PromptInfo mPromptInfo; + @VisibleForTesting final BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger; private final long mRequestId; private final long mOperationId; private final int mUserId; @@ -164,6 +165,32 @@ public final class AuthSession implements IBinder.DeathRecipient { @NonNull PromptInfo promptInfo, boolean debugEnabled, @NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties) { + this(context, biometricContext, statusBarService, sysuiReceiver, keystore, random, + clientDeathReceiver, preAuthInfo, token, requestId, operationId, userId, + sensorReceiver, clientReceiver, opPackageName, promptInfo, debugEnabled, + fingerprintSensorProperties, BiometricFrameworkStatsLogger.getInstance()); + } + + @VisibleForTesting + AuthSession(@NonNull Context context, + @NonNull BiometricContext biometricContext, + @NonNull IStatusBarService statusBarService, + @NonNull IBiometricSysuiReceiver sysuiReceiver, + @NonNull KeyStore keystore, + @NonNull Random random, + @NonNull ClientDeathReceiver clientDeathReceiver, + @NonNull PreAuthInfo preAuthInfo, + @NonNull IBinder token, + long requestId, + long operationId, + int userId, + @NonNull IBiometricSensorReceiver sensorReceiver, + @NonNull IBiometricServiceReceiver clientReceiver, + @NonNull String opPackageName, + @NonNull PromptInfo promptInfo, + boolean debugEnabled, + @NonNull List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties, + @NonNull BiometricFrameworkStatsLogger logger) { Slog.d(TAG, "Creating AuthSession with: " + preAuthInfo); mContext = context; mBiometricContext = biometricContext; @@ -184,6 +211,7 @@ public final class AuthSession implements IBinder.DeathRecipient { mDebugEnabled = debugEnabled; mFingerprintSensorProperties = fingerprintSensorProperties; mCancelled = false; + mBiometricFrameworkStatsLogger = logger; try { mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */); @@ -708,7 +736,7 @@ public final class AuthSession implements IBinder.DeathRecipient { + ", Latency: " + latency); } - BiometricFrameworkStatsLogger.getInstance().authenticate( + mBiometricFrameworkStatsLogger.authenticate( mBiometricContext.updateContext(new OperationContextExt(true /* isBP */), isCrypto()), statsModality(), @@ -723,11 +751,17 @@ public final class AuthSession implements IBinder.DeathRecipient { } else { final long latency = System.currentTimeMillis() - mStartTimeMs; - int error = reason == BiometricPrompt.DISMISSED_REASON_NEGATIVE - ? BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON - : reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL - ? BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED - : 0; + int error = 0; + switch(reason) { + case BiometricPrompt.DISMISSED_REASON_NEGATIVE: + error = BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON; + break; + case BiometricPrompt.DISMISSED_REASON_USER_CANCEL: + error = BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED; + break; + default: + } + if (DEBUG) { Slog.v(TAG, "Dismissed! Modality: " + statsModality() + ", User: " + mUserId @@ -739,17 +773,19 @@ public final class AuthSession implements IBinder.DeathRecipient { + ", Latency: " + latency); } // Auth canceled - BiometricFrameworkStatsLogger.getInstance().error( - mBiometricContext.updateContext(new OperationContextExt(true /* isBP */), - isCrypto()), - statsModality(), - BiometricsProtoEnums.ACTION_AUTHENTICATE, - BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, - mDebugEnabled, - latency, - error, - 0 /* vendorCode */, - mUserId); + if (error != 0) { + mBiometricFrameworkStatsLogger.error( + mBiometricContext.updateContext(new OperationContextExt(true /* isBP */), + isCrypto()), + statsModality(), + BiometricsProtoEnums.ACTION_AUTHENTICATE, + BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, + mDebugEnabled, + latency, + error, + 0 /* vendorCode */, + mUserId); + } } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java index 4268eb924225..662477ddbbe9 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -18,6 +18,9 @@ package com.android.server.biometrics; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_NEGATIVE_BUTTON; +import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED; +import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED; import static android.hardware.biometrics.BiometricPrompt.DISMISSED_REASON_NEGATIVE; import static com.android.server.biometrics.BiometricServiceStateProto.STATE_AUTH_CALLED; @@ -32,6 +35,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyObject; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -45,6 +49,7 @@ import android.app.trust.ITrustManager; import android.content.Context; import android.content.res.Resources; import android.hardware.biometrics.BiometricManager.Authenticators; +import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.IBiometricAuthenticator; import android.hardware.biometrics.IBiometricSensorReceiver; @@ -64,7 +69,10 @@ import android.security.KeyStore; import androidx.test.filters.SmallTest; import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricFrameworkStatsLogger; +import com.android.server.biometrics.log.OperationContextExt; import org.junit.Before; import org.junit.Test; @@ -95,6 +103,7 @@ public class AuthSessionTest { @Mock private IBiometricSysuiReceiver mSysuiReceiver; @Mock private KeyStore mKeyStore; @Mock private AuthSession.ClientDeathReceiver mClientDeathReceiver; + @Mock private BiometricFrameworkStatsLogger mBiometricFrameworkStatsLogger; private Random mRandom; private IBinder mToken; @@ -395,6 +404,84 @@ public class AuthSessionTest { eq(FingerprintManager.FINGERPRINT_ACQUIRED_VENDOR_BASE), eq(acquiredStrVendor)); } + @Test + public void testLogOnDialogDismissed_authenticatedWithConfirmation() throws RemoteException { + final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class); + + setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator); + final AuthSession session = createAuthSession(mSensors, + false /* checkDevicePolicyManager */, + Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, + 0 /* operationId */, + 0 /* userId */); + session.goToInitialState(); + assertEquals(STATE_AUTH_CALLED, session.getState()); + + session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRMED, null); + verify(mBiometricFrameworkStatsLogger, times(1)).authenticate( + (OperationContextExt) anyObject(), + eq(BiometricsProtoEnums.MODALITY_FACE), + eq(BiometricsProtoEnums.ACTION_UNKNOWN), + eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT), + eq(false), /* debugEnabled */ + anyLong(), /* latency */ + eq(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED), + eq(true), /* confirmationRequired */ + eq(0) /* userId */, + eq(-1f) /* ambientLightLux */); + } + + @Test + public void testLogOnDialogDismissed_authenticatedWithoutConfirmation() throws RemoteException { + final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class); + + setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator); + final AuthSession session = createAuthSession(mSensors, + false /* checkDevicePolicyManager */, + Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, + 0 /* operationId */, + 0 /* userId */); + session.goToInitialState(); + assertEquals(STATE_AUTH_CALLED, session.getState()); + + session.onDialogDismissed(DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED, null); + verify(mBiometricFrameworkStatsLogger, never()).authenticate( + anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(), + anyBoolean(), anyInt(), eq(-1f)); + verify(mBiometricFrameworkStatsLogger, never()).error( + anyObject(), anyInt(), anyInt(), anyInt(), anyBoolean(), anyLong(), anyInt(), + anyInt(), anyInt()); + } + + @Test + public void testLogOnDialogDismissed_error() throws RemoteException { + final IBiometricAuthenticator faceAuthenticator = mock(IBiometricAuthenticator.class); + + setupFace(0 /* id */, false /* confirmationAlwaysRequired */, faceAuthenticator); + final AuthSession session = createAuthSession(mSensors, + false /* checkDevicePolicyManager */, + Authenticators.BIOMETRIC_STRONG, + TEST_REQUEST_ID, + 0 /* operationId */, + 0 /* userId */); + session.goToInitialState(); + assertEquals(STATE_AUTH_CALLED, session.getState()); + + session.onDialogDismissed(DISMISSED_REASON_NEGATIVE, null); + verify(mBiometricFrameworkStatsLogger, times(1)).error( + (OperationContextExt) anyObject(), + eq(BiometricsProtoEnums.MODALITY_FACE), + eq(BiometricsProtoEnums.ACTION_AUTHENTICATE), + eq(BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT), + eq(false), + anyLong(), + eq(BIOMETRIC_ERROR_NEGATIVE_BUTTON), + eq(0) /* vendorCode */, + eq(0) /* userId */); + } + // TODO (b/208484275) : Enable these tests // @Test // public void testPreAuth_canAuthAndPrivacyDisabled() throws Exception { @@ -498,7 +585,7 @@ public class AuthSessionTest { return new AuthSession(mContext, mBiometricContext, mStatusBarService, mSysuiReceiver, mKeyStore, mRandom, mClientDeathReceiver, preAuthInfo, mToken, requestId, operationId, userId, mSensorReceiver, mClientReceiver, TEST_PACKAGE, promptInfo, - false /* debugEnabled */, mFingerprintSensorProps); + false /* debugEnabled */, mFingerprintSensorProps, mBiometricFrameworkStatsLogger); } private PromptInfo createPromptInfo(@Authenticators.Types int authenticators) { |