diff --git a/packages/SystemUI/res/layout/auth_biometric_view.xml b/packages/SystemUI/res/layout/auth_biometric_view.xml
deleted file mode 100644
index ee4da25f22842..0000000000000
--- a/packages/SystemUI/res/layout/auth_biometric_view.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index 1413f4a815743..74d7d5fed2d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -59,7 +59,7 @@ import java.util.Set;
/**
* Contains the Biometric views (title, subtitle, icon, buttons, etc.) and its controllers.
*/
-public class AuthBiometricView extends LinearLayout {
+public abstract class AuthBiometricView extends LinearLayout {
private static final String TAG = "AuthBiometricView";
@@ -116,7 +116,7 @@ public class AuthBiometricView extends LinearLayout {
void onAction(int action);
}
- protected final Handler mHandler;
+ private final Handler mHandler;
private final AccessibilityManager mAccessibilityManager;
private final LockPatternUtils mLockPatternUtils;
protected final int mTextColorError;
@@ -155,8 +155,8 @@ public class AuthBiometricView extends LinearLayout {
// Measurements when biometric view is showing text, buttons, etc.
@Nullable @VisibleForTesting AuthDialog.LayoutParams mLayoutParams;
- protected Callback mCallback;
- protected @BiometricState int mState;
+ private Callback mCallback;
+ @BiometricState private int mState;
private float mIconOriginalY;
@@ -250,18 +250,9 @@ public class AuthBiometricView extends LinearLayout {
return false;
}
- /**
- * Create the controller for managing the icons transitions during the prompt.
- *
- * Subclass should override.
- */
+ /** Create the controller for managing the icons transitions during the prompt.*/
@NonNull
- protected AuthIconController createIconController() {
- return new AuthIconController(mContext, mIconView) {
- @Override
- public void updateIcon(int lastState, int newState) {}
- };
- }
+ protected abstract AuthIconController createIconController();
void setPanelController(AuthPanelController panelController) {
mPanelController = panelController;
@@ -881,4 +872,14 @@ public class AuthBiometricView extends LinearLayout {
@AuthDialog.DialogSize int getSize() {
return mSize;
}
+
+ /** If authentication has successfully occurred and the view is done. */
+ boolean isAuthenticated() {
+ return mState == STATE_AUTHENTICATED;
+ }
+
+ /** If authentication is currently in progress. */
+ boolean isAuthenticating() {
+ return mState == STATE_AUTHENTICATING;
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
new file mode 100644
index 0000000000000..328ad39cddd56
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2022 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 com.android.systemui.biometrics
+
+import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE
+import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class AuthBiometricFingerprintAndFaceViewTest : SysuiTestCase() {
+
+ @JvmField @Rule
+ var mockitoRule = MockitoJUnit.rule()
+
+ @Mock
+ private lateinit var callback: AuthBiometricView.Callback
+ @Mock
+ private lateinit var panelController: AuthPanelController
+
+ private lateinit var biometricView: AuthBiometricFingerprintAndFaceView
+
+ @Before
+ fun setup() {
+ biometricView = R.layout.auth_biometric_fingerprint_and_face_view
+ .asTestAuthBiometricView(mContext, callback, panelController)
+ waitForIdleSync()
+ }
+
+ @After
+ fun tearDown() {
+ biometricView.destroyDialog()
+ }
+
+ @Test
+ fun fingerprintSuccessDoesNotRequireExplicitConfirmation() {
+ biometricView.onDialogAnimatedIn()
+ biometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT)
+ waitForIdleSync()
+
+ assertThat(biometricView.isAuthenticated).isTrue()
+ verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED)
+ }
+
+ @Test
+ fun faceSuccessRequiresExplicitConfirmation() {
+ biometricView.onDialogAnimatedIn()
+ biometricView.onAuthenticationSucceeded(TYPE_FACE)
+ waitForIdleSync()
+
+ assertThat(biometricView.isAuthenticated).isFalse()
+ assertThat(biometricView.isAuthenticating).isFalse()
+ assertThat(biometricView.mConfirmButton.visibility).isEqualTo(View.GONE)
+ verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED)
+
+ // icon acts as confirm button
+ biometricView.mIconView.performClick()
+ waitForIdleSync()
+
+ assertThat(biometricView.isAuthenticated).isTrue()
+ verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED)
+ }
+
+ @Test
+ fun ignoresFaceErrors() {
+ biometricView.onDialogAnimatedIn()
+ biometricView.onError(TYPE_FACE, "not a face")
+ waitForIdleSync()
+
+ assertThat(biometricView.isAuthenticating).isTrue()
+ verify(callback, never()).onAction(AuthBiometricView.Callback.ACTION_ERROR)
+
+ biometricView.onError(TYPE_FINGERPRINT, "that's a nope")
+ waitForIdleSync()
+
+ verify(callback).onAction(AuthBiometricView.Callback.ACTION_ERROR)
+ }
+
+ override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt
new file mode 100644
index 0000000000000..687cb517b2f49
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2022 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 com.android.systemui.biometrics
+
+import android.hardware.biometrics.BiometricAuthenticator
+import android.os.Bundle
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class AuthBiometricFingerprintViewTest : SysuiTestCase() {
+
+ @JvmField @Rule
+ val mockitoRule = MockitoJUnit.rule()
+
+ @Mock
+ private lateinit var callback: AuthBiometricView.Callback
+ @Mock
+ private lateinit var panelController: AuthPanelController
+
+ private lateinit var biometricView: AuthBiometricView
+
+ private fun createView(allowDeviceCredential: Boolean = false): AuthBiometricFingerprintView {
+ val view = R.layout.auth_biometric_fingerprint_view.asTestAuthBiometricView(
+ mContext, callback, panelController, allowDeviceCredential = allowDeviceCredential
+ ) as AuthBiometricFingerprintView
+ waitForIdleSync()
+ return view
+ }
+
+ @Before
+ fun setup() {
+ biometricView = createView()
+ }
+
+ @After
+ fun tearDown() {
+ biometricView.destroyDialog()
+ }
+
+ @Test
+ fun testOnAuthenticationSucceeded_noConfirmationRequired_sendsActionAuthenticated() {
+ biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
+ waitForIdleSync()
+
+ assertThat(biometricView.isAuthenticated).isTrue()
+ verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED)
+ }
+
+ @Test
+ fun testOnAuthenticationSucceeded_confirmationRequired_updatesDialogContents() {
+ biometricView.setRequireConfirmation(true)
+ biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
+ waitForIdleSync()
+
+ // TODO: this should be tested in the subclasses
+ if (biometricView.supportsRequireConfirmation()) {
+ verify(callback, never()).onAction(ArgumentMatchers.anyInt())
+ assertThat(biometricView.mNegativeButton.visibility).isEqualTo(View.GONE)
+ assertThat(biometricView.mCancelButton.visibility).isEqualTo(View.VISIBLE)
+ assertThat(biometricView.mCancelButton.isEnabled).isTrue()
+ assertThat(biometricView.mConfirmButton.isEnabled).isTrue()
+ assertThat(biometricView.mIndicatorView.text)
+ .isEqualTo(mContext.getText(R.string.biometric_dialog_tap_confirm))
+ assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE)
+ } else {
+ assertThat(biometricView.isAuthenticated).isTrue()
+ verify(callback).onAction(eq(AuthBiometricView.Callback.ACTION_AUTHENTICATED))
+ }
+ }
+
+ @Test
+ fun testPositiveButton_sendsActionAuthenticated() {
+ biometricView.mConfirmButton.performClick()
+ waitForIdleSync()
+
+ verify(callback).onAction(AuthBiometricView.Callback.ACTION_AUTHENTICATED)
+ assertThat(biometricView.isAuthenticated).isTrue()
+ }
+
+ @Test
+ fun testNegativeButton_beforeAuthentication_sendsActionButtonNegative() {
+ biometricView.onDialogAnimatedIn()
+ biometricView.mNegativeButton.performClick()
+ waitForIdleSync()
+
+ verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE)
+ }
+
+ @Test
+ fun testCancelButton_whenPendingConfirmation_sendsActionUserCanceled() {
+ biometricView.setRequireConfirmation(true)
+ biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
+
+ assertThat(biometricView.mNegativeButton.visibility).isEqualTo(View.GONE)
+ biometricView.mCancelButton.performClick()
+ waitForIdleSync()
+
+ verify(callback).onAction(AuthBiometricView.Callback.ACTION_USER_CANCELED)
+ }
+
+ @Test
+ fun testTryAgainButton_sendsActionTryAgain() {
+ biometricView.mTryAgainButton.performClick()
+ waitForIdleSync()
+
+ verify(callback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN)
+ assertThat(biometricView.mTryAgainButton.visibility).isEqualTo(View.GONE)
+ assertThat(biometricView.isAuthenticating).isTrue()
+ }
+
+ @Test
+ fun testOnErrorSendsActionError() {
+ biometricView.onError(BiometricAuthenticator.TYPE_FACE, "testError")
+ waitForIdleSync()
+
+ verify(callback).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR))
+ }
+
+ @Test
+ fun testOnErrorShowsMessage() {
+ // prevent error state from instantly returning to authenticating in the test
+ biometricView.mAnimationDurationHideDialog = 10_000
+
+ val message = "another error"
+ biometricView.onError(BiometricAuthenticator.TYPE_FACE, message)
+ waitForIdleSync()
+
+ assertThat(biometricView.isAuthenticating).isFalse()
+ assertThat(biometricView.isAuthenticated).isFalse()
+ assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(biometricView.mIndicatorView.text).isEqualTo(message)
+ }
+
+ @Test
+ fun testBackgroundClicked_sendsActionUserCanceled() {
+ val view = View(mContext)
+ biometricView.setBackgroundView(view)
+ view.performClick()
+
+ verify(callback).onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED))
+ }
+
+ @Test
+ fun testBackgroundClicked_afterAuthenticated_neverSendsUserCanceled() {
+ val view = View(mContext)
+ biometricView.setBackgroundView(view)
+ biometricView.onAuthenticationSucceeded(BiometricAuthenticator.TYPE_FINGERPRINT)
+ view.performClick()
+
+ verify(callback, never())
+ .onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED))
+ }
+
+ @Test
+ fun testBackgroundClicked_whenSmallDialog_neverSendsUserCanceled() {
+ biometricView.mLayoutParams = AuthDialog.LayoutParams(0, 0)
+ biometricView.updateSize(AuthDialog.SIZE_SMALL)
+ val view = View(mContext)
+ biometricView.setBackgroundView(view)
+ view.performClick()
+
+ verify(callback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED))
+ }
+
+ @Test
+ fun testIgnoresUselessHelp() {
+ biometricView.mAnimationDurationHideDialog = 10_000
+ biometricView.onDialogAnimatedIn()
+ waitForIdleSync()
+
+ assertThat(biometricView.isAuthenticating).isTrue()
+
+ val helpText = biometricView.mIndicatorView.text
+ biometricView.onHelp(BiometricAuthenticator.TYPE_FINGERPRINT, "")
+ waitForIdleSync()
+
+ // text should not change
+ assertThat(biometricView.mIndicatorView.text).isEqualTo(helpText)
+ verify(callback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR))
+ }
+
+ @Test
+ fun testRestoresState() {
+ val requireConfirmation = true
+ biometricView.mAnimationDurationHideDialog = 10_000
+ val failureMessage = "testFailureMessage"
+ biometricView.setRequireConfirmation(requireConfirmation)
+ biometricView.onAuthenticationFailed(BiometricAuthenticator.TYPE_FACE, failureMessage)
+ waitForIdleSync()
+
+ val state = Bundle()
+ biometricView.onSaveState(state)
+ assertThat(biometricView.mTryAgainButton.visibility).isEqualTo(View.GONE)
+ assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY))
+ .isEqualTo(View.GONE)
+ assertThat(state.getInt(AuthDialog.KEY_BIOMETRIC_STATE))
+ .isEqualTo(AuthBiometricView.STATE_ERROR)
+ assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE)
+ assertThat(state.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)).isTrue()
+ assertThat(biometricView.mIndicatorView.text).isEqualTo(failureMessage)
+ assertThat(state.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING))
+ .isEqualTo(failureMessage)
+
+ // TODO: Test dialog size. Should move requireConfirmation to buildBiometricPromptBundle
+
+ // Create new dialog and restore the previous state into it
+ biometricView.destroyDialog()
+ biometricView = createView()
+ biometricView.restoreState(state)
+ biometricView.mAnimationDurationHideDialog = 10_000
+ biometricView.setRequireConfirmation(requireConfirmation)
+ waitForIdleSync()
+
+ assertThat(biometricView.mTryAgainButton.visibility).isEqualTo(View.GONE)
+ assertThat(biometricView.mIndicatorView.visibility).isEqualTo(View.VISIBLE)
+
+ // TODO: Test restored text. Currently cannot test this, since it gets restored only after
+ // dialog size is known.
+ }
+
+ @Test
+ fun testCredentialButton_whenDeviceCredentialAllowed() {
+ biometricView.destroyDialog()
+ biometricView = createView(allowDeviceCredential = true)
+
+ assertThat(biometricView.mUseCredentialButton.visibility).isEqualTo(View.VISIBLE)
+ assertThat(biometricView.mNegativeButton.visibility).isEqualTo(View.GONE)
+
+ biometricView.mUseCredentialButton.performClick()
+ waitForIdleSync()
+
+ verify(callback).onAction(AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL)
+ }
+
+ override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages()
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
deleted file mode 100644
index f99b20d47ab8e..0000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * Copyright (C) 2019 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 com.android.systemui.biometrics;
-
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricManager.Authenticators;
-
-import static com.android.systemui.biometrics.AuthBiometricView.Callback.ACTION_AUTHENTICATED;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-
-import android.hardware.biometrics.PromptInfo;
-import android.os.Bundle;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
-import android.testing.ViewUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.SysuiTestCase;
-
-import org.junit.After;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.junit.MockitoJUnit;
-import org.mockito.junit.MockitoRule;
-
-@RunWith(AndroidTestingRunner.class)
-@RunWithLooper
-@SmallTest
-public class AuthBiometricViewTest extends SysuiTestCase {
-
- @Rule
- public final MockitoRule mMockitoRule = MockitoJUnit.rule();
-
- @Mock
- private AuthBiometricView.Callback mCallback;
- @Mock
- private AuthPanelController mPanelController;
-
- private AuthBiometricView mBiometricView;
-
- @After
- public void tearDown() {
- destroyDialog();
- }
-
- @Test
- public void testOnAuthenticationSucceeded_noConfirmationRequired_sendsActionAuthenticated() {
- initDialog(false /* allowDeviceCredential */, mCallback);
-
- // The onAuthenticated runnable is posted when authentication succeeds.
- mBiometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT);
- waitForIdleSync();
- assertEquals(AuthBiometricView.STATE_AUTHENTICATED, mBiometricView.mState);
- verify(mCallback).onAction(ACTION_AUTHENTICATED);
- }
-
- @Test
- public void testOnAuthenticationSucceeded_confirmationRequired_updatesDialogContents() {
- initDialog(false /* allowDeviceCredential */, mCallback);
-
- mBiometricView.setRequireConfirmation(true);
- mBiometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT);
- waitForIdleSync();
-
- // TODO: this should be tested in the subclasses
- if (mBiometricView.supportsRequireConfirmation()) {
- assertEquals(AuthBiometricView.STATE_PENDING_CONFIRMATION, mBiometricView.mState);
-
- verify(mCallback, never()).onAction(anyInt());
-
- assertEquals(View.GONE, mBiometricView.mNegativeButton.getVisibility());
- assertEquals(View.VISIBLE, mBiometricView.mCancelButton.getVisibility());
- assertTrue(mBiometricView.mCancelButton.isEnabled());
-
- assertTrue(mBiometricView.mConfirmButton.isEnabled());
- assertEquals(mContext.getText(R.string.biometric_dialog_tap_confirm),
- mBiometricView.mIndicatorView.getText());
- assertEquals(View.VISIBLE, mBiometricView.mIndicatorView.getVisibility());
- } else {
- assertEquals(AuthBiometricView.STATE_AUTHENTICATED, mBiometricView.mState);
- verify(mCallback).onAction(eq(ACTION_AUTHENTICATED));
- }
-
- }
-
- @Test
- public void testPositiveButton_sendsActionAuthenticated() {
- initDialog(false /* allowDeviceCredential */, mCallback);
-
- mBiometricView.mConfirmButton.performClick();
- waitForIdleSync();
-
- verify(mCallback).onAction(ACTION_AUTHENTICATED);
- assertEquals(AuthBiometricView.STATE_AUTHENTICATED, mBiometricView.mState);
- }
-
- @Test
- public void testNegativeButton_beforeAuthentication_sendsActionButtonNegative() {
- initDialog(false /* allowDeviceCredential */, mCallback);
-
- mBiometricView.onDialogAnimatedIn();
- mBiometricView.mNegativeButton.performClick();
- waitForIdleSync();
-
- verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE);
- }
-
- @Test
- public void testCancelButton_whenPendingConfirmation_sendsActionUserCanceled() {
- initDialog(false /* allowDeviceCredential */, mCallback);
-
- mBiometricView.setRequireConfirmation(true);
- mBiometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT);
-
- assertEquals(View.GONE, mBiometricView.mNegativeButton.getVisibility());
-
- mBiometricView.mCancelButton.performClick();
- waitForIdleSync();
-
- verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_USER_CANCELED);
- }
-
- @Test
- public void testTryAgainButton_sendsActionTryAgain() {
- initDialog(false /* allowDeviceCredential */, mCallback);
-
- mBiometricView.mTryAgainButton.performClick();
- waitForIdleSync();
-
- verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN);
- assertEquals(AuthBiometricView.STATE_AUTHENTICATING, mBiometricView.mState);
- }
-
- @Test
- @Ignore("flaky, b/189031816")
- public void testError_sendsActionError() {
- initDialog(false /* allowDeviceCredential */, mCallback);
- final String testError = "testError";
- mBiometricView.onError(TYPE_FACE, testError);
- waitForIdleSync();
-
- verify(mCallback).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR));
- assertEquals(AuthBiometricView.STATE_IDLE, mBiometricView.mState);
- }
-
- @Test
- public void testBackgroundClicked_sendsActionUserCanceled() {
- initDialog(false /* allowDeviceCredential */, mCallback);
-
- View view = new View(mContext);
- mBiometricView.setBackgroundView(view);
- view.performClick();
- verify(mCallback).onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED));
- }
-
- @Test
- public void testBackgroundClicked_afterAuthenticated_neverSendsUserCanceled() {
- initDialog(false /* allowDeviceCredential */, mCallback);
-
- View view = new View(mContext);
- mBiometricView.setBackgroundView(view);
- mBiometricView.onAuthenticationSucceeded(TYPE_FINGERPRINT);
- view.performClick();
- verify(mCallback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED));
- }
-
- @Test
- public void testBackgroundClicked_whenSmallDialog_neverSendsUserCanceled() {
- initDialog(false /* allowDeviceCredential */, mCallback);
- mBiometricView.mLayoutParams = new AuthDialog.LayoutParams(0, 0);
- mBiometricView.updateSize(AuthDialog.SIZE_SMALL);
-
- View view = new View(mContext);
- mBiometricView.setBackgroundView(view);
- view.performClick();
- verify(mCallback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED));
- }
-
- @Test
- public void testIgnoresUselessHelp() {
- initDialog(false /* allowDeviceCredential */, mCallback);
-
- mBiometricView.onDialogAnimatedIn();
- waitForIdleSync();
-
- assertEquals(AuthBiometricView.STATE_AUTHENTICATING, mBiometricView.mState);
-
- mBiometricView.onHelp(TYPE_FINGERPRINT, "");
- waitForIdleSync();
-
- assertEquals("", mBiometricView.mIndicatorView.getText());
- verify(mCallback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_ERROR));
- assertEquals(AuthBiometricView.STATE_AUTHENTICATING, mBiometricView.mState);
- }
-
- @Test
- public void testRestoresState() {
- final boolean requireConfirmation = true;
-
- initDialog(false /* allowDeviceCredential */, mCallback, null, 10000);
-
- final String failureMessage = "testFailureMessage";
- mBiometricView.setRequireConfirmation(requireConfirmation);
- mBiometricView.onAuthenticationFailed(TYPE_FACE, failureMessage);
- waitForIdleSync();
-
- Bundle state = new Bundle();
- mBiometricView.onSaveState(state);
-
- assertEquals(View.GONE, mBiometricView.mTryAgainButton.getVisibility());
- assertEquals(View.GONE, state.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY));
-
- assertEquals(AuthBiometricView.STATE_ERROR, mBiometricView.mState);
- assertEquals(AuthBiometricView.STATE_ERROR, state.getInt(AuthDialog.KEY_BIOMETRIC_STATE));
-
- assertEquals(View.VISIBLE, mBiometricView.mIndicatorView.getVisibility());
- assertTrue(state.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING));
-
- assertEquals(failureMessage, mBiometricView.mIndicatorView.getText());
- assertEquals(failureMessage, state.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING));
-
- // TODO: Test dialog size. Should move requireConfirmation to buildBiometricPromptBundle
-
- // Create new dialog and restore the previous state into it
- destroyDialog();
- initDialog(false /* allowDeviceCredential */, mCallback, state, 10000);
- mBiometricView.mAnimationDurationHideDialog = 10000;
- mBiometricView.setRequireConfirmation(requireConfirmation);
- waitForIdleSync();
-
- assertEquals(View.GONE, mBiometricView.mTryAgainButton.getVisibility());
- assertEquals(AuthBiometricView.STATE_ERROR, mBiometricView.mState);
- assertEquals(View.VISIBLE, mBiometricView.mIndicatorView.getVisibility());
-
- // TODO: Test restored text. Currently cannot test this, since it gets restored only after
- // dialog size is known.
- }
-
- @Test
- public void testCredentialButton_whenDeviceCredentialAllowed() throws InterruptedException {
- initDialog(true /* allowDeviceCredential */, mCallback);
-
- assertEquals(View.VISIBLE, mBiometricView.mUseCredentialButton.getVisibility());
- assertEquals(View.GONE, mBiometricView.mNegativeButton.getVisibility());
- mBiometricView.mUseCredentialButton.performClick();
- waitForIdleSync();
-
- verify(mCallback).onAction(AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL);
- }
-
- private PromptInfo buildPromptInfo(boolean allowDeviceCredential) {
- PromptInfo promptInfo = new PromptInfo();
- promptInfo.setTitle("Title");
- int authenticators = Authenticators.BIOMETRIC_WEAK;
- if (allowDeviceCredential) {
- authenticators |= Authenticators.DEVICE_CREDENTIAL;
- } else {
- promptInfo.setNegativeButtonText("Negative");
- }
- promptInfo.setAuthenticators(authenticators);
- return promptInfo;
- }
-
- private void initDialog(boolean allowDeviceCredential, AuthBiometricView.Callback callback) {
- initDialog(allowDeviceCredential, callback,
- null /* savedState */, 0 /* hideDelay */);
- }
-
- private void initDialog(boolean allowDeviceCredential,
- AuthBiometricView.Callback callback, Bundle savedState, int hideDelay) {
- final LayoutInflater inflater = LayoutInflater.from(mContext);
- mBiometricView = (AuthBiometricView) inflater.inflate(
- R.layout.auth_biometric_view, null, false);
- mBiometricView.mAnimationDurationLong = 0;
- mBiometricView.mAnimationDurationShort = 0;
- mBiometricView.mAnimationDurationHideDialog = hideDelay;
- mBiometricView.setPromptInfo(buildPromptInfo(allowDeviceCredential));
- mBiometricView.setCallback(callback);
- mBiometricView.restoreState(savedState);
- ViewUtils.attachView(mBiometricView);
- mBiometricView.setPanelController(mPanelController);
- waitForIdleSync();
- }
-
- private void destroyDialog() {
- if (mBiometricView != null && mBiometricView.isAttachedToWindow()) {
- ViewUtils.detachView(mBiometricView);
- }
- }
-
- @Override
- protected void waitForIdleSync() {
- TestableLooper.get(this).processAllMessages();
- super.waitForIdleSync();
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 2341928b2565d..bbc3968cbd242 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -16,13 +16,11 @@
package com.android.systemui.biometrics
import android.app.admin.DevicePolicyManager
+import android.hardware.biometrics.BiometricAuthenticator
import android.hardware.biometrics.BiometricConstants
import android.hardware.biometrics.BiometricManager
-import android.hardware.biometrics.ComponentInfoInternal
import android.hardware.biometrics.PromptInfo
-import android.hardware.biometrics.SensorProperties
import android.hardware.face.FaceSensorPropertiesInternal
-import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.os.Handler
import android.os.IBinder
@@ -40,7 +38,6 @@ import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -57,12 +54,12 @@ import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
-@RunWithLooper
+@RunWithLooper(setAsMainLooper = true)
@SmallTest
class AuthContainerViewTest : SysuiTestCase() {
@JvmField @Rule
- var rule = MockitoJUnit.rule()
+ var mockitoRule = MockitoJUnit.rule()
@Mock
lateinit var callback: AuthDialogCallback
@@ -86,13 +83,13 @@ class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testNotifiesAnimatedIn() {
- initializeContainer()
+ initializeFingerprintContainer()
verify(callback).onDialogAnimatedIn()
}
@Test
fun testIgnoresAnimatedInWhenDismissed() {
- val container = initializeContainer(addToView = false)
+ val container = initializeFingerprintContainer(addToView = false)
container.dismissFromSystemServer()
waitForIdleSync()
@@ -107,7 +104,7 @@ class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testActionAuthenticated_sendsDismissedAuthenticated() {
- val container = initializeContainer()
+ val container = initializeFingerprintContainer()
container.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_AUTHENTICATED
)
@@ -122,7 +119,7 @@ class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testActionUserCanceled_sendsDismissedUserCanceled() {
- val container = initializeContainer()
+ val container = initializeFingerprintContainer()
container.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_USER_CANCELED
)
@@ -140,7 +137,7 @@ class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testActionButtonNegative_sendsDismissedButtonNegative() {
- val container = initializeContainer()
+ val container = initializeFingerprintContainer()
container.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_BUTTON_NEGATIVE
)
@@ -155,7 +152,9 @@ class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testActionTryAgain_sendsTryAgain() {
- val container = initializeContainer(BiometricManager.Authenticators.BIOMETRIC_WEAK)
+ val container = initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
+ )
container.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN
)
@@ -166,8 +165,8 @@ class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testActionError_sendsDismissedError() {
- val container = initializeContainer()
- authContainer!!.mBiometricCallback.onAction(
+ val container = initializeFingerprintContainer()
+ container.mBiometricCallback.onAction(
AuthBiometricView.Callback.ACTION_ERROR
)
waitForIdleSync()
@@ -181,8 +180,8 @@ class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testActionUseDeviceCredential_sendsOnDeviceCredentialPressed() {
- val container = initializeContainer(
- BiometricManager.Authenticators.BIOMETRIC_WEAK or
+ val container = initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or
BiometricManager.Authenticators.DEVICE_CREDENTIAL
)
container.mBiometricCallback.onAction(
@@ -196,8 +195,8 @@ class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testAnimateToCredentialUI_invokesStartTransitionToCredentialUI() {
- val container = initializeContainer(
- BiometricManager.Authenticators.BIOMETRIC_WEAK or
+ val container = initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or
BiometricManager.Authenticators.DEVICE_CREDENTIAL
)
container.animateToCredentialUI()
@@ -208,7 +207,7 @@ class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testShowBiometricUI() {
- val container = initializeContainer()
+ val container = initializeFingerprintContainer()
waitForIdleSync()
@@ -218,7 +217,9 @@ class AuthContainerViewTest : SysuiTestCase() {
@Test
fun testShowCredentialUI() {
- val container = initializeContainer(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
+ val container = initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
waitForIdleSync()
assertThat(container.hasCredentialView()).isTrue()
@@ -232,7 +233,9 @@ class AuthContainerViewTest : SysuiTestCase() {
DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
)
- val container = initializeContainer(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
+ val container = initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
waitForIdleSync()
assertThat(container.hasCredentialPatternView()).isTrue()
@@ -249,7 +252,9 @@ class AuthContainerViewTest : SysuiTestCase() {
// In the credential view, clicking on the background (to cancel authentication) is not
// valid. Thus, the listener should be null, and it should not be in the accessibility
// hierarchy.
- val container = initializeContainer(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
+ val container = initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
waitForIdleSync()
assertThat(container.hasCredentialPasswordView()).isTrue()
@@ -277,49 +282,52 @@ class AuthContainerViewTest : SysuiTestCase() {
assertThat((layoutParams.fitInsetsTypes and WindowInsets.Type.ime()) == 0).isTrue()
}
- private fun initializeContainer(
+ @Test
+ fun coexFaceRestartsOnTouch() {
+ val container = initializeCoexContainer()
+
+ container.onPointerDown()
+ waitForIdleSync()
+
+ container.onAuthenticationFailed(BiometricAuthenticator.TYPE_FACE, "failed")
+ waitForIdleSync()
+
+ verify(callback, never()).onTryAgainPressed()
+
+ container.onPointerDown()
+ waitForIdleSync()
+
+ verify(callback).onTryAgainPressed()
+ }
+
+ private fun initializeFingerprintContainer(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
addToView: Boolean = true
+ ) = initializeContainer(
+ TestAuthContainerView(
+ authenticators = authenticators,
+ fingerprintProps = fingerprintSensorPropertiesInternal()
+ ),
+ addToView
+ )
+
+ private fun initializeCoexContainer(
+ authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
+ addToView: Boolean = true
+ ) = initializeContainer(
+ TestAuthContainerView(
+ authenticators = authenticators,
+ fingerprintProps = fingerprintSensorPropertiesInternal(),
+ faceProps = faceSensorPropertiesInternal()
+ ),
+ addToView
+ )
+
+ private fun initializeContainer(
+ view: TestAuthContainerView,
+ addToView: Boolean
): TestAuthContainerView {
- val config = AuthContainerView.Config()
- config.mContext = mContext
- config.mCallback = callback
- config.mSensorIds = intArrayOf(0)
- config.mSkipAnimation = true
- config.mPromptInfo = PromptInfo()
- config.mPromptInfo.authenticators = authenticators
- val componentInfo = listOf(
- ComponentInfoInternal(
- "faceSensor" /* componentId */,
- "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
- "00000001" /* serialNumber */, "" /* softwareVersion */
- ),
- ComponentInfoInternal(
- "matchingAlgorithm" /* componentId */,
- "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
- "vendor/version/revision" /* softwareVersion */
- )
- )
- val fpProps = listOf(
- FingerprintSensorPropertiesInternal(
- 0,
- SensorProperties.STRENGTH_STRONG,
- 5 /* maxEnrollmentsPerUser */,
- componentInfo,
- FingerprintSensorProperties.TYPE_REAR,
- false /* resetLockoutRequiresHardwareAuthToken */
- )
- )
- authContainer = TestAuthContainerView(
- config,
- fpProps,
- listOf(),
- wakefulnessLifecycle,
- userManager,
- lockPatternUtils,
- Handler(TestableLooper.get(this).looper),
- FakeExecutor(FakeSystemClock())
- )
+ authContainer = view
if (addToView) {
authContainer!!.addToView()
@@ -329,27 +337,34 @@ class AuthContainerViewTest : SysuiTestCase() {
}
private inner class TestAuthContainerView(
- config: Config,
- fpProps: List,
- faceProps: List,
- wakefulnessLifecycle: WakefulnessLifecycle,
- userManager: UserManager,
- lockPatternUtils: LockPatternUtils,
- mainHandler: Handler,
- bgExecutor: DelayableExecutor
+ authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
+ fingerprintProps: List = listOf(),
+ faceProps: List = listOf()
) : AuthContainerView(
- config, fpProps, faceProps,
- wakefulnessLifecycle, userManager, lockPatternUtils, mainHandler, bgExecutor
+ Config().apply {
+ mContext = this@AuthContainerViewTest.context
+ mCallback = callback
+ mSensorIds = (fingerprintProps.map { it.sensorId } +
+ faceProps.map { it.sensorId }).toIntArray()
+ mSkipAnimation = true
+ mPromptInfo = PromptInfo().apply {
+ this.authenticators = authenticators
+ }
+ },
+ fingerprintProps,
+ faceProps,
+ wakefulnessLifecycle,
+ userManager,
+ lockPatternUtils,
+ Handler(TestableLooper.get(this).looper),
+ FakeExecutor(FakeSystemClock())
) {
override fun postOnAnimation(runnable: Runnable) {
runnable.run()
}
}
- override fun waitForIdleSync() {
- TestableLooper.get(this).processAllMessages()
- super.waitForIdleSync()
- }
+ override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages()
private fun AuthContainerView.addToView() {
ViewUtils.attachView(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
index 57f36172c998f..8820c164cba4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt
@@ -16,27 +16,123 @@
package com.android.systemui.biometrics
+import android.annotation.IdRes
+import android.content.Context
+import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.ComponentInfoInternal
-import android.hardware.biometrics.SensorLocationInternal
+import android.hardware.biometrics.PromptInfo
import android.hardware.biometrics.SensorProperties
+import android.hardware.face.FaceSensorPropertiesInternal
+import android.hardware.face.FaceSensorProperties
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.os.Bundle
-/** Creates properties from the sensor location with test values. */
-fun SensorLocationInternal.asFingerprintSensorProperties(
- sensorId: Int = 22,
- @SensorProperties.Strength sensorStrength: Int = SensorProperties.STRENGTH_WEAK,
- @FingerprintSensorProperties.SensorType sensorType: Int =
- FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
- maxEnrollmentsPerUser: Int = 1,
- info: List = listOf(ComponentInfoInternal("a", "b", "c", "d", "e")),
- resetLockoutRequiresHardwareAuthToken: Boolean = false
-) = FingerprintSensorPropertiesInternal(
- sensorId,
- sensorStrength,
- maxEnrollmentsPerUser,
- info,
- sensorType,
- resetLockoutRequiresHardwareAuthToken,
- listOf(this)
-)
+import android.testing.ViewUtils
+import android.view.LayoutInflater
+
+/**
+ * Inflate the given BiometricPrompt layout and initialize it with test parameters.
+ *
+ * This attaches the view so be sure to call [destroyDialog] at the end of the test.
+ */
+@IdRes
+internal fun Int.asTestAuthBiometricView(
+ context: Context,
+ callback: AuthBiometricView.Callback,
+ panelController: AuthPanelController,
+ allowDeviceCredential: Boolean = false,
+ savedState: Bundle? = null,
+ hideDelay: Int = 0
+): T {
+ val view = LayoutInflater.from(context).inflate(this, null, false) as T
+ view.mAnimationDurationLong = 0
+ view.mAnimationDurationShort = 0
+ view.mAnimationDurationHideDialog = hideDelay
+ view.setPromptInfo(buildPromptInfo(allowDeviceCredential))
+ view.setCallback(callback)
+ view.restoreState(savedState)
+ view.setPanelController(panelController)
+
+ ViewUtils.attachView(view)
+
+ return view
+}
+
+private fun buildPromptInfo(allowDeviceCredential: Boolean): PromptInfo {
+ val promptInfo = PromptInfo()
+ promptInfo.title = "Title"
+ var authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK
+ if (allowDeviceCredential) {
+ authenticators = authenticators or BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ } else {
+ promptInfo.negativeButtonText = "Negative"
+ }
+ promptInfo.authenticators = authenticators
+ return promptInfo
+}
+
+/** Detach the view, if needed. */
+internal fun AuthBiometricView?.destroyDialog() {
+ if (this != null && isAttachedToWindow) {
+ ViewUtils.detachView(this)
+ }
+}
+
+/** Create [FingerprintSensorPropertiesInternal] for a test. */
+internal fun fingerprintSensorPropertiesInternal(
+ ids: List = listOf(0)
+): List {
+ val componentInfo = listOf(
+ ComponentInfoInternal(
+ "fingerprintSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */
+ ),
+ ComponentInfoInternal(
+ "matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */
+ )
+ )
+ return ids.map { id ->
+ FingerprintSensorPropertiesInternal(
+ id,
+ SensorProperties.STRENGTH_STRONG,
+ 5 /* maxEnrollmentsPerUser */,
+ componentInfo,
+ FingerprintSensorProperties.TYPE_REAR,
+ false /* resetLockoutRequiresHardwareAuthToken */
+ )
+ }
+}
+
+/** Create [FaceSensorPropertiesInternal] for a test. */
+internal fun faceSensorPropertiesInternal(
+ ids: List = listOf(1)
+): List {
+ val componentInfo = listOf(
+ ComponentInfoInternal(
+ "faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */, "" /* softwareVersion */
+ ),
+ ComponentInfoInternal(
+ "matchingAlgorithm" /* componentId */,
+ "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
+ "vendor/version/revision" /* softwareVersion */
+ )
+ )
+ return ids.map { id ->
+ FaceSensorPropertiesInternal(
+ id,
+ SensorProperties.STRENGTH_STRONG,
+ 2 /* maxEnrollmentsPerUser */,
+ componentInfo,
+ FaceSensorProperties.TYPE_RGB,
+ true /* supportsFaceDetection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresHardwareAuthToken */
+ )
+ }
+}