diff options
3 files changed, 122 insertions, 5 deletions
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index fa678fc5ee1a..2e40f6096ccb 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -142,6 +142,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan private PromptInfo mPromptInfo; private ButtonInfo mNegativeButtonInfo; private Context mContext; + private IAuthService mService; /** * Creates a builder for a {@link BiometricPrompt} dialog. @@ -212,6 +213,18 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } /** + * @param service + * @return This builder. + * @hide + */ + @RequiresPermission(TEST_BIOMETRIC) + @NonNull + public Builder setService(@NonNull IAuthService service) { + mService = service; + return this; + } + + /** * Sets an optional title, subtitle, and/or description that will override other text when * the user is authenticating with PIN/pattern/password. Currently for internal use only. * @return This builder. @@ -472,7 +485,9 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan throw new IllegalArgumentException("Can't have both negative button behavior" + " and device credential enabled"); } - return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo); + mService = (mService == null) ? IAuthService.Stub.asInterface( + ServiceManager.getService(Context.AUTH_SERVICE)) : mService; + return new BiometricPrompt(mContext, mPromptInfo, mNegativeButtonInfo, mService); } } @@ -521,7 +536,6 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan public void onAuthenticationFailed() { mExecutor.execute(() -> { mAuthenticationCallback.onAuthenticationFailed(); - mIsPromptShowing = false; }); } @@ -604,12 +618,12 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan private boolean mIsPromptShowing; - private BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo) { + private BiometricPrompt(Context context, PromptInfo promptInfo, ButtonInfo negativeButtonInfo, + IAuthService service) { mContext = context; mPromptInfo = promptInfo; mNegativeButtonInfo = negativeButtonInfo; - mService = IAuthService.Stub.asInterface( - ServiceManager.getService(Context.AUTH_SERVICE)); + mService = service; mIsPromptShowing = false; } diff --git a/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java new file mode 100644 index 000000000000..66f3bca72aeb --- /dev/null +++ b/core/tests/coretests/src/android/hardware/biometrics/BiometricPromptTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 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 android.hardware.biometrics; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.CancellationSignal; +import android.os.Handler; +import android.os.RemoteException; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.junit.MockitoRule; + +import java.util.concurrent.Executor; + + +@Presubmit +@RunWith(MockitoJUnitRunner.class) +public class BiometricPromptTest { + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private Context mContext; + @Mock + private IAuthService mService; + private BiometricPrompt mBiometricPrompt; + + private CancellationSignal mCancellationSignal; + + private final TestLooper mLooper = new TestLooper(); + private final Handler mHandler = new Handler(mLooper.getLooper()); + private final Executor mExecutor = mHandler::post; + + @Before + public void setUp() throws RemoteException { + mBiometricPrompt = new BiometricPrompt.Builder(mContext) + .setUseDefaultSubtitle() + .setUseDefaultTitle() + .setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG + | BiometricManager.Authenticators.DEVICE_CREDENTIAL) + .setService(mService) + .build(); + + mCancellationSignal = new CancellationSignal(); + when(mService.authenticate(any(), anyLong(), anyInt(), any(), anyString(), any())) + .thenReturn(0L); + when(mContext.getPackageName()).thenReturn("BiometricPromptTest"); + } + + @Test + public void testCancellationAfterAuthenticationFailed() throws RemoteException { + ArgumentCaptor<IBiometricServiceReceiver> biometricServiceReceiverCaptor = + ArgumentCaptor.forClass(IBiometricServiceReceiver.class); + BiometricPrompt.AuthenticationCallback callback = + new BiometricPrompt.AuthenticationCallback() { + @Override + public void onAuthenticationError(int errorCode, CharSequence errString) { + super.onAuthenticationError(errorCode, errString); + }}; + mBiometricPrompt.authenticate(mCancellationSignal, mExecutor, callback); + mLooper.dispatchAll(); + + verify(mService).authenticate(any(), anyLong(), anyInt(), + biometricServiceReceiverCaptor.capture(), anyString(), any()); + + biometricServiceReceiverCaptor.getValue().onAuthenticationFailed(); + mLooper.dispatchAll(); + mCancellationSignal.cancel(); + + verify(mService).cancelAuthentication(any(), anyString(), anyLong()); + } +} diff --git a/core/tests/coretests/src/android/hardware/biometrics/OWNERS b/core/tests/coretests/src/android/hardware/biometrics/OWNERS new file mode 100644 index 000000000000..6a2192a2c7fb --- /dev/null +++ b/core/tests/coretests/src/android/hardware/biometrics/OWNERS @@ -0,0 +1 @@ +include /services/core/java/com/android/server/biometrics/OWNERS |