From 88cf7037b0d9e96cfdb7ffa2c7158e23b636e60e Mon Sep 17 00:00:00 2001 From: lbill Date: Tue, 22 Oct 2024 09:17:46 +0000 Subject: [PATCH] [Screen off unlock UDFPS] Fingerprint Settings integration 2/2 1. Integrate FingerprintSettings with Toggle 2. Sync the Toggle state with SettingProvider key "screen_off_unlock_udfps" Reference: go/udfps-aof #Settings UI design(Deck) Bug: 373792870 Bug: 369939804 Bug: 369938501 Flag: android.hardware.biometrics.screen_off_unlock_udfps Test: atest FingerprintSettingsFragmentTest atest DevelopmentSettingsDashboardFragmentTest atest FingerprintSettingsUnlockCategoryControllerTest atest FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest Test: adb shell settings put secure screen_off_unlock_udfps <1|0> Change-Id: I03794f53684bfb60b4a854e14507e67f60c55a7d --- Android.bp | 1 + res/values/strings.xml | 10 ++ res/xml/security_settings_fingerprint.xml | 11 +- .../fingerprint/FingerprintSettings.java | 102 +++++++++--- ...eenOffUnlockUdfpsPreferenceController.java | 119 +++++++++++++ .../FingerprintUnlockCategoryController.java | 4 +- ...ffUnlockUdfpsPreferenceControllerTest.java | 157 ++++++++++++++++++ ...tSettingsUnlockCategoryControllerTest.java | 58 ++++++- 8 files changed, 436 insertions(+), 26 deletions(-) create mode 100644 src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceController.java create mode 100644 tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest.java diff --git a/Android.bp b/Android.bp index 087030b37cf..e81ef9e2f06 100644 --- a/Android.bp +++ b/Android.bp @@ -79,6 +79,7 @@ android_library { "BiometricsSharedLib", "SystemUIUnfoldLib", "WifiTrackerLib", + "android.hardware.biometrics.flags-aconfig-java", "android.hardware.dumpstate-V1-java", "android.hardware.dumpstate-V1.0-java", "android.hardware.dumpstate-V1.1-java", diff --git a/res/values/strings.xml b/res/values/strings.xml index 75e901e4e1b..99548de2122 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1234,6 +1234,16 @@ Can\u2019t use fingerprint sensor Visit a repair provider. + + + security_settings_screen_off_unlock_udfps + + Screen-off Fingerprint Unlock + + Use Fingerprint Unlock even when the screen is off + + Screen-off, Unlock + More security settings diff --git a/res/xml/security_settings_fingerprint.xml b/res/xml/security_settings_fingerprint.xml index 9c8b0a3f9ea..32f09244f97 100644 --- a/res/xml/security_settings_fingerprint.xml +++ b/res/xml/security_settings_fingerprint.xml @@ -40,7 +40,16 @@ android:title="@string/security_settings_require_screen_on_to_auth_title" android:summary="@string/security_settings_require_screen_on_to_auth_description" settings:keywords="@string/security_settings_require_screen_on_to_auth_keywords" - settings:controller="com.android.settings.biometrics.fingerprint.FingerprintSettingsRequireScreenOnToAuthPreferenceController" /> + settings:controller="com.android.settings.biometrics.fingerprint.FingerprintSettingsRequireScreenOnToAuthPreferenceController" + settings:isPreferenceVisible="false"/> + + { + final boolean isChecked = ((TwoStatePreference) preference).isChecked(); + mScreenOffUnlockUdfpsPreferenceController.setChecked(!isChecked); + return true; + }); + } + private void updatePreferencesAfterFingerprintRemoved() { updateAddPreference(); - if (isSfps()) { + if (isSfps() || screenOffUnlockUdfps()) { updateFingerprintUnlockCategoryVisibility(); } updatePreferences(); @@ -954,6 +997,18 @@ public class FingerprintSettings extends SubSettings { controller; } + } + } else if (screenOffUnlockUdfps()) { + for (AbstractPreferenceController controller : controllers) { + if (controller.getPreferenceKey() == KEY_FINGERPRINT_UNLOCK_CATEGORY) { + mFingerprintUnlockCategoryPreferenceController = + (FingerprintUnlockCategoryController) controller; + } else if (controller.getPreferenceKey() == KEY_SCREEN_OFF_FINGERPRINT_UNLOCK) { + mScreenOffUnlockUdfpsPreferenceController = + (FingerprintSettingsScreenOffUnlockUdfpsPreferenceController) + controller; + } + } } return controllers; @@ -1070,7 +1125,8 @@ public class FingerprintSettings extends SubSettings { } else if (requestCode == BIOMETRIC_AUTH_REQUEST) { mBiometricsAuthenticationRequested = false; if (resultCode != RESULT_OK) { - if (resultCode == ConfirmDeviceCredentialActivity.BIOMETRIC_LOCKOUT_ERROR_RESULT) { + if (resultCode + == ConfirmDeviceCredentialActivity.BIOMETRIC_LOCKOUT_ERROR_RESULT) { IdentityCheckBiometricErrorDialog .showBiometricErrorDialogAndFinishActivityOnDismiss(getActivity(), Utils.BiometricStatus.LOCKOUT); @@ -1408,7 +1464,7 @@ public class FingerprintSettings extends SubSettings { getContext().getSystemService(DevicePolicyManager.class); String messageId = isProfileChallengeUser ? WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE - : UNDEFINED; + : UNDEFINED; int defaultMessageId = isProfileChallengeUser ? R.string.fingerprint_last_delete_message_profile_challenge : R.string.fingerprint_last_delete_message; @@ -1417,7 +1473,7 @@ public class FingerprintSettings extends SubSettings { .setTitle(title) .setMessage(devicePolicyManager.getResources().getString( messageId, - () -> message + "\n\n" + getContext().getString(defaultMessageId))) + () -> message + "\n\n" + getContext().getString(defaultMessageId))) .setPositiveButton( R.string.security_settings_fingerprint_enroll_dialog_delete, new DialogInterface.OnClickListener() { diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceController.java new file mode 100644 index 00000000000..5c32d90ba7e --- /dev/null +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceController.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2024 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.settings.biometrics.fingerprint; + +import static android.hardware.biometrics.Flags.screenOffUnlockUdfps; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.hardware.fingerprint.FingerprintManager; +import android.os.UserHandle; +import android.provider.Settings; + +import androidx.annotation.NonNull; +import androidx.preference.Preference; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.Utils; +import com.android.settings.search.BaseSearchIndexProvider; +import com.android.settingslib.search.SearchIndexable; + +/** + * Preference controller that controls whether show screen off UDFPS unlock toggle for users to + * turn this feature ON or OFF + */ +@SearchIndexable +public class FingerprintSettingsScreenOffUnlockUdfpsPreferenceController + extends FingerprintSettingsPreferenceController { + private static final String TAG = + "FingerprintSettingsScreenOffUnlockUdfpsPreferenceController"; + + @VisibleForTesting + protected FingerprintManager mFingerprintManager; + + public FingerprintSettingsScreenOffUnlockUdfpsPreferenceController( + @NonNull Context context, @NonNull String prefKey) { + super(context, prefKey); + mFingerprintManager = Utils.getFingerprintManagerOrNull(context); + } + + @Override + public boolean isChecked() { + if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) { + return false; + } else if (getRestrictingAdmin() != null) { + return false; + } + final boolean defEnabled = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_screen_off_udfps_enabled); + final int value = Settings.Secure.getIntForUser( + mContext.getContentResolver(), + Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, + defEnabled ? 1 : 0 /* config_screen_off_udfps_enabled */, + getUserHandle()); + return value == 1; + } + + @Override + public boolean setChecked(boolean isChecked) { + Settings.Secure.putIntForUser( + mContext.getContentResolver(), + Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, + isChecked ? 1 : 0, + getUserHandle()); + return true; + } + + @Override + public void updateState(@NonNull Preference preference) { + super.updateState(preference); + if (!FingerprintSettings.isFingerprintHardwareDetected(mContext)) { + preference.setEnabled(false); + } else if (!mFingerprintManager.hasEnrolledTemplates(getUserId())) { + preference.setEnabled(false); + } else if (getRestrictingAdmin() != null) { + preference.setEnabled(false); + } else { + preference.setEnabled(true); + } + } + + @SuppressLint("MissingPermission") + @Override + public int getAvailabilityStatus() { + if (mFingerprintManager != null + && mFingerprintManager.isHardwareDetected() + && screenOffUnlockUdfps() + && !mFingerprintManager.isPowerbuttonFps()) { + return mFingerprintManager.hasEnrolledTemplates(getUserId()) + ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; + } else { + return UNSUPPORTED_ON_DEVICE; + } + } + + private int getUserHandle() { + return UserHandle.of(getUserId()).getIdentifier(); + } + + /** + * This feature is not directly searchable. + */ + public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = + new BaseSearchIndexProvider() {}; + +} diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java b/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java index 674a0dfa758..c949d3da4d8 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintUnlockCategoryController.java @@ -16,6 +16,8 @@ package com.android.settings.biometrics.fingerprint; +import static android.hardware.biometrics.Flags.screenOffUnlockUdfps; + import android.content.Context; import android.hardware.fingerprint.FingerprintManager; @@ -42,7 +44,7 @@ public class FingerprintUnlockCategoryController extends BasePreferenceControlle public int getAvailabilityStatus() { if (mFingerprintManager != null && mFingerprintManager.isHardwareDetected() - && mFingerprintManager.isPowerbuttonFps()) { + && (mFingerprintManager.isPowerbuttonFps() || screenOffUnlockUdfps())) { return mFingerprintManager.hasEnrolledTemplates(getUserId()) ? AVAILABLE : CONDITIONALLY_UNAVAILABLE; } else { diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest.java new file mode 100644 index 00000000000..7660e889db3 --- /dev/null +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 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.settings.biometrics.fingerprint; + +import static com.android.settings.core.BasePreferenceController.AVAILABLE; +import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; +import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.hardware.fingerprint.FingerprintManager; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; +import android.provider.Settings; + +import com.android.settings.testutils.shadow.ShadowUtils; +import com.android.settingslib.RestrictedSwitchPreference; + +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.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +@RunWith(RobolectricTestRunner.class) +@Config(shadows = {ShadowUtils.class}) +public class FingerprintSettingsScreenOffUnlockUdfpsPreferenceControllerTest { + + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Mock + private FingerprintManager mFingerprintManager; + @Mock + private PackageManager mPackageManager; + @Mock + private RestrictedSwitchPreference mPreference; + + private Context mContext; + private FingerprintSettingsScreenOffUnlockUdfpsPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(eq(Context.FINGERPRINT_SERVICE))).thenReturn( + mFingerprintManager); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + + mController = spy(new FingerprintSettingsScreenOffUnlockUdfpsPreferenceController(mContext, + "test_key")); + ReflectionHelpers.setField(mController, "mFingerprintManager", mFingerprintManager); + } + + @After + public void tearDown() { + ShadowUtils.reset(); + } + + @Test + public void onPreferenceChange_settingIsUpdated() { + boolean state = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, 1) != 0; + + assertThat(mController.isChecked()).isFalse(); + assertThat(mController.onPreferenceChange(mPreference, !state)).isTrue(); + boolean newState = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED, 1) != 0; + assertThat(newState).isEqualTo(!state); + } + + @Test + @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS) + public void isAvailable_isEnabled_whenUdfpsHardwareDetected_AndHasEnrolledFingerprints() { + assertThat(mController.isAvailable()).isEqualTo(false); + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + configure_hardwareDetected_isUdfps_hasEnrolledTemplates( + true /* isHardwareDetected */, + false /* isPowerbuttonFps false implies udfps */, + true /* hasEnrolledTemplates */); + assertThat(mController.isAvailable()).isEqualTo(true); + assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + + @Test + @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS) + public void isUnavailable_isDisabled_whenUdfpsHardwareDetected_AndNoEnrolledFingerprints() { + assertThat(mController.isAvailable()).isEqualTo(false); + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + configure_hardwareDetected_isUdfps_hasEnrolledTemplates( + true /* isHardwareDetected */, + false /* isPowerbuttonFps false implies udfps */, + false /* hasEnrolledTemplates */); + assertThat(mController.isAvailable()).isEqualTo(false); + assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); + } + + @Test + @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS) + public void isUnavailable_whenHardwareNotDetected() { + assertThat(mController.isAvailable()).isFalse(); + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + configure_hardwareDetected_isUdfps_hasEnrolledTemplates( + false /* isHardwareDetected */, + false /* isPowerbuttonFps false implies udfps */, + true /* hasEnrolledTemplates */); + assertThat(mController.isAvailable()).isEqualTo(false); + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + } + + @Test + @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS) + public void isUnavailable_onNonUdfpsDevice() { + assertThat(mController.isAvailable()).isFalse(); + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + configure_hardwareDetected_isUdfps_hasEnrolledTemplates( + true /* isHardwareDetected */, + true /* isPowerbuttonFps false implies udfps */, + true /* hasEnrolledTemplates */); + assertThat(mController.isAvailable()).isFalse(); + assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); + } + + private void configure_hardwareDetected_isUdfps_hasEnrolledTemplates( + boolean isHardwareDetected, boolean isPowerbuttonFps, boolean hasEnrolledTemplates) { + when(mFingerprintManager.isHardwareDetected()).thenReturn(isHardwareDetected); + when(mFingerprintManager.isPowerbuttonFps()).thenReturn(isPowerbuttonFps); + when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(hasEnrolledTemplates); + } + +} diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java index 7b6a70ed7ef..59af9346a29 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsUnlockCategoryControllerTest.java @@ -30,12 +30,15 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.fingerprint.FingerprintManager; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import com.android.settings.testutils.shadow.ShadowUtils; import com.android.settingslib.RestrictedSwitchPreference; 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; @@ -49,6 +52,8 @@ import org.robolectric.util.ReflectionHelpers; @Config(shadows = {ShadowUtils.class}) public class FingerprintSettingsUnlockCategoryControllerTest { + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Mock private FingerprintManager mFingerprintManager; @Mock @@ -59,6 +64,8 @@ public class FingerprintSettingsUnlockCategoryControllerTest { private Context mContext; private FingerprintSettingsRequireScreenOnToAuthPreferenceController mController; + private FingerprintSettingsScreenOffUnlockUdfpsPreferenceController mScreenOffUnlockController; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -69,7 +76,12 @@ public class FingerprintSettingsUnlockCategoryControllerTest { mController = spy(new FingerprintSettingsRequireScreenOnToAuthPreferenceController(mContext, "test_key")); + mScreenOffUnlockController = spy( + new FingerprintSettingsScreenOffUnlockUdfpsPreferenceController(mContext, + "screen_off_unlock_test_key")); ReflectionHelpers.setField(mController, "mFingerprintManager", mFingerprintManager); + ReflectionHelpers.setField(mScreenOffUnlockController, "mFingerprintManager", + mFingerprintManager); } @After @@ -89,6 +101,20 @@ public class FingerprintSettingsUnlockCategoryControllerTest { assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } + @Test + @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS) + public void isAvailable_isEnabled_whenUdfpsHardwareDetected_AndHasEnrolledFingerprints() { + assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false); + assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo( + UNSUPPORTED_ON_DEVICE); + configure_hardwareDetected_isSfps_hasEnrolledTemplates( + true /* isHardwareDetected */, + false /* isPowerbuttonFps false implies udfps */, + true /* hasEnrolledTemplates */); + assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(true); + assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo(AVAILABLE); + } + @Test public void isUnavailable_isDisabled_whenSfpsHardwareDetected_AndNoEnrolledFingerprints() { assertThat(mController.isAvailable()).isEqualTo(false); @@ -102,7 +128,22 @@ public class FingerprintSettingsUnlockCategoryControllerTest { } @Test - public void isUnavailable_whenHardwareNotDetected() { + @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS) + public void isUnavailable_isDisabled_whenUdfpsHardwareDetected_AndNoEnrolledFingerprints() { + assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false); + assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo( + UNSUPPORTED_ON_DEVICE); + configure_hardwareDetected_isSfps_hasEnrolledTemplates( + true /* isHardwareDetected */, + false /* isPowerbuttonFps false implies udfps */, + false /* hasEnrolledTemplates */); + assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false); + assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo( + CONDITIONALLY_UNAVAILABLE); + } + + @Test + public void isUnavailable_whenHardwareNotDetected_onSfpsDevice() { assertThat(mController.isAvailable()).isFalse(); assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); configure_hardwareDetected_isSfps_hasEnrolledTemplates( @@ -113,6 +154,21 @@ public class FingerprintSettingsUnlockCategoryControllerTest { assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); } + @Test + @EnableFlags(android.hardware.biometrics.Flags.FLAG_SCREEN_OFF_UNLOCK_UDFPS) + public void isUnavailable_whenHardwareNotDetected_onUdfpsDevice() { + assertThat(mScreenOffUnlockController.isAvailable()).isFalse(); + assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo( + UNSUPPORTED_ON_DEVICE); + configure_hardwareDetected_isSfps_hasEnrolledTemplates( + false /* isHardwareDetected */, + false /* isPowerbuttonFps false implies udfps */, + true /* hasEnrolledTemplates */); + assertThat(mScreenOffUnlockController.isAvailable()).isEqualTo(false); + assertThat(mScreenOffUnlockController.getAvailabilityStatus()).isEqualTo( + UNSUPPORTED_ON_DEVICE); + } + @Test public void isUnavailable_onNonSfpsDevice() { assertThat(mController.isAvailable()).isFalse();