diff options
| author | 2025-01-19 19:05:06 -0800 | |
|---|---|---|
| committer | 2025-01-19 19:05:06 -0800 | |
| commit | d4ad87bf60f3ea23fb0740713068241d15204b51 (patch) | |
| tree | 26546b56a73c944a2e9899690587c49046f10f5a | |
| parent | 71378c186c95c4c4f3bea01625ed5ae5f10d2070 (diff) | |
| parent | 4998c5adcaf276cf7721496238c9798a775afc44 (diff) | |
Merge "Split enabledForApp & enabledOnKeyguard settings for FP and Face" into main
15 files changed, 552 insertions, 84 deletions
diff --git a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl index 1268658d75f5..cf25b7ec85e1 100644 --- a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl +++ b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl @@ -20,5 +20,5 @@ package android.hardware.biometrics; * @hide */ oneway interface IBiometricEnabledOnKeyguardCallback { - void onChanged(boolean enabled, int userId); + void onChanged(boolean enabled, int userId, int modality); }
\ No newline at end of file diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2b18b01734d3..2e231e3957c6 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11380,19 +11380,56 @@ public final class Settings { /** * Whether or not biometric is allowed on Keyguard. + * + * @deprecated Use {@link #FINGERPRINT_KEYGUARD_ENABLED} or {@link #FACE_KEYGUARD_ENABLED} + * instead. + * * @hide */ + @Deprecated @Readable public static final String BIOMETRIC_KEYGUARD_ENABLED = "biometric_keyguard_enabled"; /** * Whether or not biometric is allowed for apps (through BiometricPrompt). + * + * @deprecated Use {@link #FINGERPRINT_APP_ENABLED} or {@link #FACE_APP_ENABLED} instead. + * * @hide */ + @Deprecated @Readable public static final String BIOMETRIC_APP_ENABLED = "biometric_app_enabled"; /** + * Whether or not fingerprint is allowed on Keyguard. + * @hide + */ + @Readable + public static final String FINGERPRINT_KEYGUARD_ENABLED = "fingerprint_keyguard_enabled"; + + /** + * Whether or not fingerprint is allowed for apps (through BiometricPrompt). + * @hide + */ + @Readable + public static final String FINGERPRINT_APP_ENABLED = "fingerptint_app_enabled"; + + /** + * Whether or not face is allowed on Keyguard. + * @hide + */ + @Readable + public static final String FACE_KEYGUARD_ENABLED = "face_keyguard_enabled"; + + /** + * Whether or not face is allowed for apps (through BiometricPrompt). + * @hide + */ + @Readable + public static final String FACE_APP_ENABLED = "face_app_enabled"; + + /** * Whether or not mandatory biometrics is enabled. * @hide */ diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 640829eacf9f..d936a5c699c7 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -287,5 +287,9 @@ public class SecureSettings { Settings.Secure.ADVANCED_PROTECTION_MODE, Settings.Secure.ACCESSIBILITY_KEY_GESTURE_TARGETS, Settings.Secure.EM_VALUE, + Settings.Secure.FACE_APP_ENABLED, + Settings.Secure.FACE_KEYGUARD_ENABLED, + Settings.Secure.FINGERPRINT_APP_ENABLED, + Settings.Secure.FINGERPRINT_KEYGUARD_ENABLED, }; } diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 9fd0cc8e2812..919c3c4721f2 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -456,5 +456,9 @@ public class SecureSettingsValidators { new InclusiveIntegerRangeValidator(0, 1)); VALIDATORS.put(Secure.ADVANCED_PROTECTION_MODE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.FACE_APP_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.FACE_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.FINGERPRINT_APP_ENABLED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.FINGERPRINT_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR); } } diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 30dcd09d35ab..0a7d880677d8 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -521,6 +521,7 @@ android_library { "androidx.compose.animation_animation-graphics", "androidx.lifecycle_lifecycle-viewmodel-compose", "kairos", + "aconfig_settings_flags_lib", ], libs: [ "keepanno-annotations", @@ -706,6 +707,7 @@ android_library { "androidx.lifecycle_lifecycle-viewmodel-compose", "TraceurCommon", "Traceur-res", + "aconfig_settings_flags_lib", ], } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt index 69fb03dc6433..4e8a2a349283 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt @@ -22,8 +22,12 @@ import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT import android.content.Intent import android.content.pm.UserInfo +import android.hardware.biometrics.BiometricAuthenticator.TYPE_ANY_BIOMETRIC +import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE +import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT import android.hardware.biometrics.BiometricManager import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback +import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -75,6 +79,8 @@ import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.stub @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -153,7 +159,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { fun fingerprintEnrollmentChange() = testScope.runTest { createBiometricSettingsRepository() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT) val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled) runCurrent() @@ -173,7 +179,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { fun fingerprintEnabledStateChange() = testScope.runTest { createBiometricSettingsRepository() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT) val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled) runCurrent() @@ -183,12 +189,50 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { assertThat(fingerprintAllowed()).isTrue() // when biometrics are not enabled by settings - biometricsAreNotEnabledBySettings() + biometricsAreNotEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT) assertThat(fingerprintAllowed()).isFalse() // when biometrics are enabled by settings - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT) + assertThat(fingerprintAllowed()).isTrue() + } + + @Test + @EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + fun enabledStateChange_typeFingerprintEnabled_typeFaceDisabled() = + testScope.runTest { + createBiometricSettingsRepository() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT) + val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled) + val faceAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) + runCurrent() + + // start state + authController.stub { + on { isFingerprintEnrolled(anyInt()) } doReturn true + } + enrollmentChange(UNDER_DISPLAY_FINGERPRINT, PRIMARY_USER_ID, true) assertThat(fingerprintAllowed()).isTrue() + assertThat(faceAllowed()).isFalse() + } + + @Test + @EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + fun enabledStateChange_typeFingerprintDisabled_typeFaceEnabled() = + testScope.runTest { + createBiometricSettingsRepository() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FACE) + val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled) + val faceAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) + runCurrent() + + // start state + authController.stub { + on { isFaceAuthEnrolled(anyInt()) } doReturn true + } + enrollmentChange(FACE, PRIMARY_USER_ID, true) + assertThat(fingerprintAllowed()).isFalse() + assertThat(faceAllowed()).isTrue() } @Test @@ -197,7 +241,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { fingerprintIsEnrolled() doNotDisableKeyguardAuthFeatures() createBiometricSettingsRepository() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT) val strongBiometricAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed) @@ -220,7 +264,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { createBiometricSettingsRepository() val convenienceFaceAuthAllowed by collectLastValue(underTest.isFaceAuthCurrentlyAllowed) doNotDisableKeyguardAuthFeatures() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FACE) onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) onNonStrongAuthChanged(true, PRIMARY_USER_ID) @@ -282,7 +326,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { faceAuthIsEnrolled() createBiometricSettingsRepository() doNotDisableKeyguardAuthFeatures() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FACE) runCurrent() val convenienceBiometricAllowed by @@ -315,7 +359,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { testScope.runTest { fingerprintIsEnrolled(PRIMARY_USER_ID) createBiometricSettingsRepository() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT) val fingerprintEnabledByDevicePolicy = collectLastValue(underTest.isFingerprintEnrolledAndEnabled) @@ -341,7 +385,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { createBiometricSettingsRepository() val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FACE) doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID) @@ -376,16 +420,22 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { assertThat(faceAuthAllowed()).isTrue() } - private fun biometricsAreEnabledBySettings(userId: Int = PRIMARY_USER_ID) { + private fun biometricsAreEnabledBySettings( + userId: Int = PRIMARY_USER_ID, + modality: Int = TYPE_ANY_BIOMETRIC, + ) { verify(biometricManager, atLeastOnce()) .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) - biometricManagerCallback.value.onChanged(true, userId) + biometricManagerCallback.value.onChanged(true, userId, modality) } - private fun biometricsAreNotEnabledBySettings(userId: Int = PRIMARY_USER_ID) { + private fun biometricsAreNotEnabledBySettings( + userId: Int = PRIMARY_USER_ID, + modality: Int = TYPE_ANY_BIOMETRIC, + ) { verify(biometricManager, atLeastOnce()) .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) - biometricManagerCallback.value.onChanged(false, userId) + biometricManagerCallback.value.onChanged(false, userId, modality) } @Test @@ -413,7 +463,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { userRepository.setSelectedUserInfo(ANOTHER_USER) doNotDisableKeyguardAuthFeatures(ANOTHER_USER_ID) - biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID) + biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID, TYPE_FACE) onNonStrongAuthChanged(true, ANOTHER_USER_ID) whenever(authController.isFaceAuthEnrolled(ANOTHER_USER_ID)).thenReturn(true) enrollmentChange(FACE, ANOTHER_USER_ID, true) @@ -441,7 +491,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { assertThat(isFaceAuthAllowed()).isFalse() - biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID) + biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID, TYPE_FACE) runCurrent() assertThat(isFaceAuthAllowed()).isFalse() @@ -458,7 +508,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { faceAuthIsEnrolled() createBiometricSettingsRepository() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FACE) doNotDisableKeyguardAuthFeatures() mobileConnectionsRepository.fake.isAnySimSecure.value = false runCurrent() @@ -485,7 +535,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { deviceIsInPostureThatSupportsFaceAuth() doNotDisableKeyguardAuthFeatures() faceAuthIsStrongBiometric() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FACE) mobileConnectionsRepository.fake.isAnySimSecure.value = false onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) @@ -512,12 +562,12 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { assertThat(isFaceAuthAllowed()).isFalse() // Value changes for another user - biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID) + biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID, TYPE_FACE) assertThat(isFaceAuthAllowed()).isFalse() // Value changes for current user. - biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID) + biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID, TYPE_FACE) assertThat(isFaceAuthAllowed()).isTrue() } @@ -537,13 +587,13 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) val isFingerprintEnrolledAndEnabled = collectLastValue(underTest.isFingerprintEnrolledAndEnabled) - biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID) + biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID, TYPE_FINGERPRINT) runCurrent() userRepository.setSelectedUserInfo(ANOTHER_USER) runCurrent() assertThat(isFingerprintEnrolledAndEnabled()).isFalse() - biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID) + biometricManagerCallback.value.onChanged(true, PRIMARY_USER_ID, TYPE_FINGERPRINT) runCurrent() userRepository.setSelectedUserInfo(PRIMARY_USER) runCurrent() @@ -559,7 +609,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { verify(biometricManager) .registerEnabledOnKeyguardCallback(biometricManagerCallback.capture()) val isFaceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled) - biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID) + biometricManagerCallback.value.onChanged(true, ANOTHER_USER_ID, TYPE_FACE) runCurrent() userRepository.setSelectedUserInfo(ANOTHER_USER) @@ -691,7 +741,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { deviceIsInPostureThatSupportsFaceAuth() doNotDisableKeyguardAuthFeatures() faceAuthIsStrongBiometric() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FACE) onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) onNonStrongAuthChanged(false, PRIMARY_USER_ID) @@ -715,7 +765,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { deviceIsInPostureThatSupportsFaceAuth() doNotDisableKeyguardAuthFeatures() faceAuthIsNonStrongBiometric() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FACE) onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID) onNonStrongAuthChanged(false, PRIMARY_USER_ID) @@ -737,7 +787,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { fun fpAuthCurrentlyAllowed_dependsOnNonStrongAuthBiometricSetting_ifFpIsNotStrong() = testScope.runTest { createBiometricSettingsRepository() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT) val isFingerprintCurrentlyAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed) @@ -779,7 +829,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { fun fpAuthCurrentlyAllowed_dependsOnStrongAuthBiometricSetting_ifFpIsStrong() = testScope.runTest { createBiometricSettingsRepository() - biometricsAreEnabledBySettings() + biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT) val isFingerprintCurrentlyAllowed by collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed) diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index 7d291c311ca3..61038ef1a72d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -437,8 +437,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback = new IBiometricEnabledOnKeyguardCallback.Stub() { @Override - public void onChanged(boolean enabled, int userId) { + public void onChanged(boolean enabled, int userId, int modality) { mHandler.post(() -> { + if (com.android.settings.flags.Flags.biometricsOnboardingEducation() + && modality != TYPE_FINGERPRINT) { + return; + } mBiometricEnabledForUser.put(userId, enabled); updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); }); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt index ab8cc7125d6e..4e7de5dd8a9c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt @@ -20,6 +20,9 @@ import android.app.admin.DevicePolicyManager import android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED import android.content.Context import android.content.IntentFilter +import android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE +import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT +import android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE import android.hardware.biometrics.BiometricManager import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback import android.os.UserHandle @@ -141,6 +144,8 @@ constructor( ) : BiometricSettingsRepository, Dumpable { private val biometricsEnabledForUser = mutableMapOf<Int, Boolean>() + private val fingerprintEnabledForUser = mutableMapOf<Int, Boolean>() + private val faceEnabledForUser = mutableMapOf<Int, Boolean>() override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean> @@ -246,10 +251,25 @@ constructor( } } - private val areBiometricsEnabledForCurrentUser: Flow<Boolean> = + private val isFingerprintEnabledForCurrentUser: Flow<Boolean> = userRepository.selectedUserInfo.flatMapLatest { userInfo -> areBiometricsEnabledForDeviceEntryFromUserSetting.map { - biometricsEnabledForUser[userInfo.id] ?: false + if (com.android.settings.flags.Flags.biometricsOnboardingEducation()) { + fingerprintEnabledForUser[userInfo.id] ?: false + } else { + biometricsEnabledForUser[userInfo.id] ?: false + } + } + } + + private val isFaceEnabledForCurrentUser: Flow<Boolean> = + userRepository.selectedUserInfo.flatMapLatest { userInfo -> + areBiometricsEnabledForDeviceEntryFromUserSetting.map { + if (com.android.settings.flags.Flags.biometricsOnboardingEducation()) { + faceEnabledForUser[userInfo.id] ?: false + } else { + biometricsEnabledForUser[userInfo.id] ?: false + } } } @@ -264,31 +284,44 @@ constructor( .distinctUntilChanged() private val isFaceAuthenticationEnabled: Flow<Boolean> = - combine(areBiometricsEnabledForCurrentUser, isFaceEnabledByDevicePolicy) { + combine(isFaceEnabledForCurrentUser, isFaceEnabledByDevicePolicy) { biometricsManagerSetting, devicePolicySetting -> biometricsManagerSetting && devicePolicySetting } - private val areBiometricsEnabledForDeviceEntryFromUserSetting: Flow<Pair<Int, Boolean>> = + private val areBiometricsEnabledForDeviceEntryFromUserSetting: Flow<Triple<Int, Boolean, Int>> = conflatedCallbackFlow { val callback = object : IBiometricEnabledOnKeyguardCallback.Stub() { - override fun onChanged(enabled: Boolean, userId: Int) { + override fun onChanged(enabled: Boolean, userId: Int, modality: Int) { trySendWithFailureLogging( - Pair(userId, enabled), + Triple(userId, enabled, modality), TAG, - "biometricsEnabled state changed" + "biometricsEnabled state changed", ) } } biometricManager?.registerEnabledOnKeyguardCallback(callback) awaitClose {} } - .onEach { biometricsEnabledForUser[it.first] = it.second } + .onEach { + if (com.android.settings.flags.Flags.biometricsOnboardingEducation()) { + when (it.third) { + TYPE_FACE -> { + faceEnabledForUser[it.first] = it.second + } + TYPE_FINGERPRINT -> { + fingerprintEnabledForUser[it.first] = it.second + } + } + } else { + biometricsEnabledForUser[it.first] = it.second + } + } // This is because the callback is binder-based and we want to avoid multiple callbacks // being registered. - .stateIn(scope, SharingStarted.Eagerly, Pair(0, false)) + .stateIn(scope, SharingStarted.Eagerly, Triple(0, false, TYPE_NONE)) private val isStrongBiometricAllowed: StateFlow<Boolean> = strongAuthTracker.isStrongBiometricAllowed.stateIn( @@ -333,7 +366,7 @@ constructor( override val isFingerprintEnrolledAndEnabled: StateFlow<Boolean> = isFingerprintEnrolled - .and(areBiometricsEnabledForCurrentUser) + .and(isFingerprintEnabledForCurrentUser) .and(isFingerprintEnabledByDevicePolicy) .stateIn(scope, SharingStarted.Eagerly, false) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 839a2bdf5588..2645811fa4ad 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -18,6 +18,8 @@ package com.android.keyguard; import static android.app.StatusBarManager.SESSION_KEYGUARD; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_ANY_BIOMETRIC; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT; @@ -1507,6 +1509,72 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test + @EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void listenForFingerprint_whenEnabledForUser_typeFingerprint() + throws RemoteException { + // GIVEN keyguard showing + mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); + mKeyguardUpdateMonitor.setKeyguardShowing(true, false); + + biometricsEnabledForCurrentUser(true, TYPE_FINGERPRINT); + mTestableLooper.processAllMessages(); + + assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(true); + } + + @Test + @EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void doNotListenForFingerprint_whenDisabledForUser_typeFingerprint() + throws RemoteException { + // GIVEN keyguard showing + mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); + mKeyguardUpdateMonitor.setKeyguardShowing(true, false); + + biometricsEnabledForCurrentUser(false, TYPE_FINGERPRINT); + mTestableLooper.processAllMessages(); + + assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false); + } + + @Test + @EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void listenForFingerprint_typeFingerprintEnabled_typeFaceDisabled() + throws RemoteException { + // GIVEN keyguard showing + mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); + mKeyguardUpdateMonitor.setKeyguardShowing(true, false); + + // Enable with fingerprint + biometricsEnabledForCurrentUser(true, TYPE_FINGERPRINT); + mTestableLooper.processAllMessages(); + + // Disable with face + biometricsEnabledForCurrentUser(false, TYPE_FACE); + mTestableLooper.processAllMessages(); + + assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(true); + } + + @Test + @EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void doNotListenForFingerprint_typeFingerprintDisabled_typeFaceEnabled() + throws RemoteException { + // GIVEN keyguard showing + mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); + mKeyguardUpdateMonitor.setKeyguardShowing(true, false); + + // Enable with face + biometricsEnabledForCurrentUser(true, TYPE_FACE); + mTestableLooper.processAllMessages(); + + // Disable with fingerprint + biometricsEnabledForCurrentUser(false, TYPE_FINGERPRINT); + mTestableLooper.processAllMessages(); + + assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(true)).isEqualTo(false); + } + + @Test public void testOccludingAppFingerprintListeningState() { // GIVEN keyguard isn't visible (app occluding) mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); @@ -2464,8 +2532,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } private void biometricsEnabledForCurrentUser() throws RemoteException { - mBiometricEnabledOnKeyguardCallback.onChanged(true, - mSelectedUserInteractor.getSelectedUserId()); + biometricsEnabledForCurrentUser(true /* enabled */, TYPE_FINGERPRINT); + } + + private void biometricsEnabledForCurrentUser(boolean enabled, int modality) + throws RemoteException { + mBiometricEnabledOnKeyguardCallback.onChanged(enabled, + mSelectedUserInteractor.getSelectedUserId(), modality); } private void primaryAuthNotRequiredByStrongAuthTracker() { diff --git a/services/core/Android.bp b/services/core/Android.bp index 42385fc5bdb0..bb493370f9fc 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -230,6 +230,7 @@ java_library_static { "notification_flags_lib", "power_hint_flags_lib", "biometrics_flags_lib", + "aconfig_settings_flags_lib", "am_flags_lib", "com_android_server_accessibility_flags_lib", "//frameworks/libs/systemui:com_android_systemui_shared_flags_lib", diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index b365ef7ff61c..15b1f220bc3c 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -17,6 +17,7 @@ package com.android.server.biometrics; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_ANY_BIOMETRIC; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; import static android.hardware.biometrics.BiometricManager.Authenticators; @@ -81,6 +82,7 @@ import android.text.TextUtils; import android.util.ArraySet; import android.util.Pair; import android.util.Slog; +import android.util.SparseBooleanArray; import android.util.proto.ProtoOutputStream; import com.android.internal.R; @@ -100,8 +102,8 @@ import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; -import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; /** @@ -263,6 +265,14 @@ public class BiometricService extends SystemService { Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_KEYGUARD_ENABLED); private final Uri BIOMETRIC_APP_ENABLED = Settings.Secure.getUriFor(Settings.Secure.BIOMETRIC_APP_ENABLED); + private final Uri FACE_KEYGUARD_ENABLED = + Settings.Secure.getUriFor(Settings.Secure.FACE_KEYGUARD_ENABLED); + private final Uri FACE_APP_ENABLED = + Settings.Secure.getUriFor(Settings.Secure.FACE_APP_ENABLED); + private final Uri FINGERPRINT_KEYGUARD_ENABLED = + Settings.Secure.getUriFor(Settings.Secure.FINGERPRINT_KEYGUARD_ENABLED); + private final Uri FINGERPRINT_APP_ENABLED = + Settings.Secure.getUriFor(Settings.Secure.FINGERPRINT_APP_ENABLED); private final Uri MANDATORY_BIOMETRICS_ENABLED = Settings.Secure.getUriFor(Settings.Secure.MANDATORY_BIOMETRICS); private final Uri MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED = Settings.Secure.getUriFor( @@ -274,6 +284,10 @@ public class BiometricService extends SystemService { private final Map<Integer, Boolean> mBiometricEnabledOnKeyguard = new HashMap<>(); private final Map<Integer, Boolean> mBiometricEnabledForApps = new HashMap<>(); + private final SparseBooleanArray mFaceEnabledOnKeyguard = new SparseBooleanArray(); + private final SparseBooleanArray mFaceEnabledForApps = new SparseBooleanArray(); + private final SparseBooleanArray mFingerprintEnabledOnKeyguard = new SparseBooleanArray(); + private final SparseBooleanArray mFingerprintEnabledForApps = new SparseBooleanArray(); private final Map<Integer, Boolean> mFaceAlwaysRequireConfirmation = new HashMap<>(); private final Map<Integer, Boolean> mMandatoryBiometricsEnabled = new HashMap<>(); private final Map<Integer, Boolean> mMandatoryBiometricsRequirementsSatisfied = @@ -323,6 +337,23 @@ public class BiometricService extends SystemService { false /* notifyForDescendants */, this /* observer */, UserHandle.USER_ALL); + } else if (com.android.settings.flags.Flags.biometricsOnboardingEducation()) { + mContentResolver.registerContentObserver(FINGERPRINT_KEYGUARD_ENABLED, + false /* notifyForDescendants */, + this /* observer */, + UserHandle.USER_ALL); + mContentResolver.registerContentObserver(FACE_KEYGUARD_ENABLED, + false /* notifyForDescendants */, + this /* observer */, + UserHandle.USER_ALL); + mContentResolver.registerContentObserver(FINGERPRINT_APP_ENABLED, + false /* notifyForDescendants */, + this /* observer */, + UserHandle.USER_ALL); + mContentResolver.registerContentObserver(FACE_APP_ENABLED, + false /* notifyForDescendants */, + this /* observer */, + UserHandle.USER_ALL); } else { mContentResolver.registerContentObserver(BIOMETRIC_KEYGUARD_ENABLED, false /* notifyForDescendants */, @@ -357,7 +388,7 @@ public class BiometricService extends SystemService { userId) != 0); if (userId == ActivityManager.getCurrentUser() && !selfChange) { - notifyEnabledOnKeyguardCallbacks(userId); + notifyEnabledOnKeyguardCallbacks(userId, TYPE_FACE); } } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) { mBiometricEnabledForApps.put(userId, Settings.Secure.getIntForUser( @@ -379,7 +410,27 @@ public class BiometricService extends SystemService { userId) != 0); if (userId == ActivityManager.getCurrentUser() && !selfChange) { - notifyEnabledOnKeyguardCallbacks(userId); + notifyEnabledOnKeyguardCallbacks(userId, TYPE_ANY_BIOMETRIC); + } + } else if (FACE_KEYGUARD_ENABLED.equals(uri)) { + mFaceEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser( + mContentResolver, + Settings.Secure.FACE_KEYGUARD_ENABLED, + DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */, + userId) != 0); + + if (userId == ActivityManager.getCurrentUser() && !selfChange) { + notifyEnabledOnKeyguardCallbacks(userId, TYPE_FACE); + } + } else if (FINGERPRINT_KEYGUARD_ENABLED.equals(uri)) { + mFingerprintEnabledOnKeyguard.put(userId, Settings.Secure.getIntForUser( + mContentResolver, + Settings.Secure.FINGERPRINT_KEYGUARD_ENABLED, + DEFAULT_KEYGUARD_ENABLED ? 1 : 0 /* default */, + userId) != 0); + + if (userId == ActivityManager.getCurrentUser() && !selfChange) { + notifyEnabledOnKeyguardCallbacks(userId, TYPE_FINGERPRINT); } } else if (BIOMETRIC_APP_ENABLED.equals(uri)) { mBiometricEnabledForApps.put(userId, Settings.Secure.getIntForUser( @@ -387,6 +438,18 @@ public class BiometricService extends SystemService { Settings.Secure.BIOMETRIC_APP_ENABLED, DEFAULT_APP_ENABLED ? 1 : 0 /* default */, userId) != 0); + } else if (FACE_APP_ENABLED.equals(uri)) { + mFaceEnabledForApps.put(userId, Settings.Secure.getIntForUser( + mContentResolver, + Settings.Secure.FACE_APP_ENABLED, + DEFAULT_APP_ENABLED ? 1 : 0 /* default */, + userId) != 0); + } else if (FINGERPRINT_APP_ENABLED.equals(uri)) { + mFingerprintEnabledForApps.put(userId, Settings.Secure.getIntForUser( + mContentResolver, + Settings.Secure.FINGERPRINT_APP_ENABLED, + DEFAULT_APP_ENABLED ? 1 : 0 /* default */, + userId) != 0); } else if (MANDATORY_BIOMETRICS_ENABLED.equals(uri)) { updateMandatoryBiometricsForAllProfiles(userId); } else if (MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED.equals(uri)) { @@ -394,26 +457,60 @@ public class BiometricService extends SystemService { } } - public boolean getEnabledOnKeyguard(int userId) { - if (!mBiometricEnabledOnKeyguard.containsKey(userId)) { - if (mUseLegacyFaceOnlySettings) { - onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, userId); - } else { - onChange(true /* selfChange */, BIOMETRIC_KEYGUARD_ENABLED, userId); + public boolean getEnabledOnKeyguard(int userId, int modality) { + if (com.android.settings.flags.Flags.biometricsOnboardingEducation()) { + if (modality == TYPE_FACE) { + if (mFaceEnabledOnKeyguard.indexOfKey(userId) < 0) { + onChange(true /* selfChange */, FACE_KEYGUARD_ENABLED, userId); + } + return mFaceEnabledOnKeyguard.get(userId, DEFAULT_KEYGUARD_ENABLED); + } else if (modality == TYPE_FINGERPRINT) { + if (mFingerprintEnabledOnKeyguard.indexOfKey(userId) < 0) { + onChange(true /* selfChange */, FINGERPRINT_KEYGUARD_ENABLED, userId); + } + return mFingerprintEnabledOnKeyguard.get(userId, DEFAULT_KEYGUARD_ENABLED); + } else { // modality == TYPE_ANY_BIOMETRIC + return mFingerprintEnabledOnKeyguard.get(userId, DEFAULT_KEYGUARD_ENABLED) + || mFaceEnabledOnKeyguard.get(userId, DEFAULT_KEYGUARD_ENABLED); + } + } else { + if (!mBiometricEnabledOnKeyguard.containsKey(userId)) { + if (mUseLegacyFaceOnlySettings) { + onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, userId); + } else { + onChange(true /* selfChange */, BIOMETRIC_KEYGUARD_ENABLED, userId); + } } + return mBiometricEnabledOnKeyguard.get(userId); } - return mBiometricEnabledOnKeyguard.get(userId); } - public boolean getEnabledForApps(int userId) { - if (!mBiometricEnabledForApps.containsKey(userId)) { - if (mUseLegacyFaceOnlySettings) { - onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId); - } else { - onChange(true /* selfChange */, BIOMETRIC_APP_ENABLED, userId); + public boolean getEnabledForApps(int userId, int modality) { + if (com.android.settings.flags.Flags.biometricsOnboardingEducation()) { + if (modality == TYPE_FACE) { + if (mFaceEnabledForApps.indexOfKey(userId) < 0) { + onChange(true /* selfChange */, FACE_APP_ENABLED, userId); + } + return mFaceEnabledForApps.get(userId, DEFAULT_APP_ENABLED); + } else if (modality == TYPE_FINGERPRINT) { + if (mFingerprintEnabledForApps.indexOfKey(userId) < 0) { + onChange(true /* selfChange */, FINGERPRINT_APP_ENABLED, userId); + } + return mFingerprintEnabledForApps.get(userId, DEFAULT_APP_ENABLED); + } else { // modality == TYPE_ANY_BIOMETRIC + return mFingerprintEnabledForApps.get(userId, DEFAULT_APP_ENABLED) + || mFaceEnabledForApps.get(userId, DEFAULT_APP_ENABLED); + } + } else { + if (!mBiometricEnabledForApps.containsKey(userId)) { + if (mUseLegacyFaceOnlySettings) { + onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId); + } else { + onChange(true /* selfChange */, BIOMETRIC_APP_ENABLED, userId); + } } + return mBiometricEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED); } - return mBiometricEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED); } public boolean getConfirmationAlwaysRequired(@BiometricAuthenticator.Modality int modality, @@ -444,17 +541,16 @@ public class BiometricService extends SystemService { DEFAULT_MANDATORY_BIOMETRICS_STATUS) && mMandatoryBiometricsRequirementsSatisfied.getOrDefault(userId, DEFAULT_MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED_STATUS) - && getEnabledForApps(userId) + && getEnabledForApps(userId, TYPE_ANY_BIOMETRIC) && (mFingerprintEnrolledForUser.getOrDefault(userId, false /* default */) || mFaceEnrolledForUser.getOrDefault(userId, false /* default */)); } - void notifyEnabledOnKeyguardCallbacks(int userId) { + void notifyEnabledOnKeyguardCallbacks(int userId, int modality) { List<EnabledOnKeyguardCallback> callbacks = mCallbacks; + final boolean enabled = getEnabledOnKeyguard(userId, modality); for (int i = 0; i < callbacks.size(); i++) { - callbacks.get(i).notify( - mBiometricEnabledOnKeyguard.getOrDefault(userId, DEFAULT_KEYGUARD_ENABLED), - userId); + callbacks.get(i).notify(enabled, userId, modality); } } @@ -596,9 +692,9 @@ public class BiometricService extends SystemService { } } - void notify(boolean enabled, int userId) { + void notify(boolean enabled, int userId, int modality) { try { - mCallback.onChanged(enabled, userId); + mCallback.onChanged(enabled, userId, modality); } catch (DeadObjectException e) { Slog.w(TAG, "Death while invoking notify", e); mEnabledOnKeyguardCallbacks.remove(this); @@ -930,8 +1026,16 @@ public class BiometricService extends SystemService { try { for (UserInfo userInfo: aliveUsers) { final int userId = userInfo.id; - callback.onChanged(mSettingObserver.getEnabledOnKeyguard(userId), - userId); + if (com.android.settings.flags.Flags.biometricsOnboardingEducation()) { + callback.onChanged(mSettingObserver.getEnabledOnKeyguard(userId, TYPE_FACE), + userId, TYPE_FACE); + callback.onChanged( + mSettingObserver.getEnabledOnKeyguard(userId, TYPE_FINGERPRINT), + userId, TYPE_FINGERPRINT); + } else { + callback.onChanged(mSettingObserver.getEnabledOnKeyguard(userId, + TYPE_ANY_BIOMETRIC), userId, TYPE_ANY_BIOMETRIC); + } } } catch (RemoteException e) { Slog.w(TAG, "Remote exception", e); @@ -1309,7 +1413,15 @@ public class BiometricService extends SystemService { @Override public void onUserSwitchComplete(int newUserId) { mSettingObserver.updateContentObserver(); - mSettingObserver.notifyEnabledOnKeyguardCallbacks(newUserId); + if (com.android.settings.flags.Flags.biometricsOnboardingEducation()) { + mSettingObserver.notifyEnabledOnKeyguardCallbacks(newUserId, + TYPE_FACE); + mSettingObserver.notifyEnabledOnKeyguardCallbacks( + newUserId, TYPE_FINGERPRINT); + } else { + mSettingObserver.notifyEnabledOnKeyguardCallbacks( + newUserId, TYPE_ANY_BIOMETRIC); + } } }, BiometricService.class.getName() ); diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index 6ed1ac859501..c739118194e5 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -284,7 +284,7 @@ class PreAuthInfo { private static boolean isEnabledForApp(BiometricService.SettingObserver settingObserver, @BiometricAuthenticator.Modality int modality, int userId) { - return settingObserver.getEnabledForApps(userId); + return settingObserver.getEnabledForApps(userId, modality); } private static boolean isBiometricDisabledByDevicePolicy( 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 4ef37b9cc3cf..8b9def98fbaf 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthSessionTest.java @@ -908,7 +908,7 @@ public class AuthSessionTest { type, false /* resetLockoutRequiresHardwareAuthToken */)); - when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); + when(mSettingObserver.getEnabledForApps(anyInt(), anyInt())).thenReturn(true); } private void setupFace(int id, boolean confirmationAlwaysRequired, @@ -930,6 +930,6 @@ public class AuthSessionTest { } }); - when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); + when(mSettingObserver.getEnabledForApps(anyInt(), anyInt())).thenReturn(true); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java index 88829c1a99b3..acca4cc294b3 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java @@ -16,6 +16,7 @@ package com.android.server.biometrics; +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_ANY_BIOMETRIC; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_CREDENTIAL; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; @@ -588,7 +589,8 @@ public class BiometricServiceTest { setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); // Disabled in user settings receives onError - when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt(), anyInt())) + .thenReturn(false); invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */, null /* authenticators */, false /* useDefaultSubtitle */, false /* deviceCredentialAllowed */); @@ -602,7 +604,8 @@ public class BiometricServiceTest { // Enrolled, not disabled in settings, user requires confirmation in settings resetReceivers(); - when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt(), anyInt())) + .thenReturn(true); when(mBiometricService.mSettingObserver.getConfirmationAlwaysRequired( anyInt() /* modality */, anyInt() /* userId */)) .thenReturn(true); @@ -1493,7 +1496,8 @@ public class BiometricServiceTest { public void testCanAuthenticate_whenBiometricsNotEnabledForApps_returnsHardwareUnavailable() throws Exception { setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); - when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt(), anyInt())) + .thenReturn(false); when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) .thenReturn(true); @@ -1512,7 +1516,8 @@ public class BiometricServiceTest { @RequiresFlagsEnabled(Flags.FLAG_MANDATORY_BIOMETRICS) public void testCanAuthenticate_whenBiometricsNotEnabledForApps() throws Exception { setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); - when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt(), anyInt())) + .thenReturn(false); when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) .thenReturn(true); @@ -1741,7 +1746,8 @@ public class BiometricServiceTest { final int testId = 0; - when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt(), anyInt())) + .thenReturn(true); when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())) .thenReturn(true); @@ -1947,7 +1953,8 @@ public class BiometricServiceTest { } @Test - public void testRegisterEnabledOnKeyguardCallback() throws RemoteException { + @RequiresFlagsDisabled(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void testRegisterEnabledOnKeyguardCallback_flagDisabled() throws RemoteException { final UserInfo userInfo1 = new UserInfo(0 /* userId */, "user1" /* name */, 0 /* flags */); final UserInfo userInfo2 = new UserInfo(10 /* userId */, "user2" /* name */, 0 /* flags */); final List<UserInfo> aliveUsers = List.of(userInfo1, userInfo2); @@ -1957,10 +1964,42 @@ public class BiometricServiceTest { mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); when(mUserManager.getAliveUsers()).thenReturn(aliveUsers); - when(mBiometricService.mSettingObserver.getEnabledOnKeyguard(userInfo1.id)) + when(mBiometricService.mSettingObserver + .getEnabledOnKeyguard(userInfo1.id, TYPE_ANY_BIOMETRIC)).thenReturn(true); + when(mBiometricService.mSettingObserver + .getEnabledOnKeyguard(userInfo2.id, TYPE_ANY_BIOMETRIC)).thenReturn(false); + when(callback.asBinder()).thenReturn(mock(IBinder.class)); + + mBiometricService.mImpl.registerEnabledOnKeyguardCallback(callback); + + waitForIdle(); + + verify(callback).asBinder(); + verify(callback).onChanged(true, userInfo1.id, TYPE_ANY_BIOMETRIC); + verify(callback).onChanged(false, userInfo2.id, TYPE_ANY_BIOMETRIC); + verifyNoMoreInteractions(callback); + } + + @Test + @RequiresFlagsEnabled(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void testRegisterEnabledOnKeyguardCallback_flagEnabled() throws RemoteException { + final UserInfo userInfo1 = new UserInfo(0 /* userId */, "user1" /* name */, 0 /* flags */); + final UserInfo userInfo2 = new UserInfo(10 /* userId */, "user2" /* name */, 0 /* flags */); + final List<UserInfo> aliveUsers = List.of(userInfo1, userInfo2); + final IBiometricEnabledOnKeyguardCallback callback = + mock(IBiometricEnabledOnKeyguardCallback.class); + + mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); + + when(mUserManager.getAliveUsers()).thenReturn(aliveUsers); + when(mBiometricService.mSettingObserver.getEnabledOnKeyguard(userInfo1.id, TYPE_FACE)) .thenReturn(true); - when(mBiometricService.mSettingObserver.getEnabledOnKeyguard(userInfo2.id)) + when(mBiometricService.mSettingObserver.getEnabledOnKeyguard(userInfo1.id, + TYPE_FINGERPRINT)).thenReturn(true); + when(mBiometricService.mSettingObserver.getEnabledOnKeyguard(userInfo2.id, TYPE_FACE)) .thenReturn(false); + when(mBiometricService.mSettingObserver.getEnabledOnKeyguard(userInfo2.id, + TYPE_FINGERPRINT)).thenReturn(false); when(callback.asBinder()).thenReturn(mock(IBinder.class)); mBiometricService.mImpl.registerEnabledOnKeyguardCallback(callback); @@ -1968,8 +2007,10 @@ public class BiometricServiceTest { waitForIdle(); verify(callback).asBinder(); - verify(callback).onChanged(true, userInfo1.id); - verify(callback).onChanged(false, userInfo2.id); + verify(callback).onChanged(true, userInfo1.id, TYPE_FACE); + verify(callback).onChanged(true, userInfo1.id, TYPE_FINGERPRINT); + verify(callback).onChanged(false, userInfo2.id, TYPE_FACE); + verify(callback).onChanged(false, userInfo2.id, TYPE_FINGERPRINT); verifyNoMoreInteractions(callback); } @@ -2065,6 +2106,58 @@ public class BiometricServiceTest { userId)); } + @Test + @RequiresFlagsEnabled(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void testCanAuthenticate_faceEnabledForApps() throws Exception { + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + when(mBiometricService.mSettingObserver.getEnabledForApps(0 /* userId */, TYPE_FACE)) + .thenReturn(true); + when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) + .thenReturn(true); + + assertEquals(BiometricManager.BIOMETRIC_SUCCESS, + invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG)); + } + + @Test + @RequiresFlagsEnabled(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void testCanAuthenticate_faceDisabledForApps() throws Exception { + setupAuthForOnly(TYPE_FACE, Authenticators.BIOMETRIC_STRONG); + when(mBiometricService.mSettingObserver.getEnabledForApps(0 /* userId */, TYPE_FACE)) + .thenReturn(false); + when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) + .thenReturn(true); + + assertEquals(BiometricManager.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS, + invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG)); + } + + @Test + @RequiresFlagsEnabled(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void testCanAuthenticate_fingerprintsEnabledForApps() throws Exception { + setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG); + when(mBiometricService.mSettingObserver.getEnabledForApps(0 /* userId */, TYPE_FINGERPRINT)) + .thenReturn(true); + when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) + .thenReturn(true); + + assertEquals(BiometricManager.BIOMETRIC_SUCCESS, + invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG)); + } + + @Test + @RequiresFlagsEnabled(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void testCanAuthenticate_fingerprintsDisabledForApps() throws Exception { + setupAuthForOnly(TYPE_FINGERPRINT, Authenticators.BIOMETRIC_STRONG); + when(mBiometricService.mSettingObserver.getEnabledForApps(0 /* userId */, TYPE_FINGERPRINT)) + .thenReturn(false); + when(mTrustManager.isDeviceSecure(anyInt(), anyInt())) + .thenReturn(true); + + assertEquals(BiometricManager.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS, + invokeCanAuthenticate(mBiometricService, Authenticators.BIOMETRIC_STRONG)); + } + // Helper methods private int invokeCanAuthenticate(BiometricService service, int authenticators) @@ -2082,7 +2175,8 @@ public class BiometricServiceTest { mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); - when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt(), anyInt())) + .thenReturn(true); if ((modality & TYPE_FINGERPRINT) != 0) { when(mFingerprintAuthenticator.hasEnrolledTemplates(anyInt(), any())) @@ -2115,7 +2209,8 @@ public class BiometricServiceTest { mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider); mBiometricService.onStart(); - when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); + when(mBiometricService.mSettingObserver.getEnabledForApps(anyInt(), anyInt())) + .thenReturn(true); assertEquals(modalities.length, strengths.length); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java index f6f831f41f83..f8cf21d9b5d7 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/PreAuthInfoTest.java @@ -23,6 +23,7 @@ import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_HW_UN import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS; import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; +import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENABLED_FOR_APPS; import static com.android.server.biometrics.sensors.LockoutTracker.LOCKOUT_NONE; import static com.google.common.truth.Truth.assertThat; @@ -96,7 +97,7 @@ public class PreAuthInfoTest { when(mTrustManager.isDeviceSecure(anyInt(), anyInt())).thenReturn(true); when(mDevicePolicyManager.getKeyguardDisabledFeatures(any(), anyInt())) .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE); - when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(true); + when(mSettingObserver.getEnabledForApps(anyInt(), anyInt())).thenReturn(true); when(mSettingObserver.getMandatoryBiometricsEnabledAndRequirementsSatisfiedForUser( anyInt())).thenReturn(true); when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(true); @@ -280,7 +281,7 @@ public class PreAuthInfoTest { public void testCalculateByPriority() throws Exception { when(mFaceAuthenticator.hasEnrolledTemplates(anyInt(), any())).thenReturn(false); - when(mSettingObserver.getEnabledForApps(anyInt())).thenReturn(false); + when(mSettingObserver.getEnabledForApps(anyInt(), anyInt())).thenReturn(false); BiometricSensor faceSensor = getFaceSensor(); BiometricSensor fingerprintSensor = getFingerprintSensor(); @@ -370,6 +371,58 @@ public class PreAuthInfoTest { assertThat(preAuthInfo.getCanAuthenticateResult()).isEqualTo(BIOMETRIC_ERROR_NO_HARDWARE); } + @Test + @RequiresFlagsEnabled(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void testBiometricsEnabledForApps_fingerprintEnabled_faceDisabled() + throws Exception { + when(mSettingObserver.getEnabledForApps(USER_ID, TYPE_FINGERPRINT)).thenReturn(true); + when(mSettingObserver.getEnabledForApps(USER_ID, TYPE_FACE)).thenReturn(false); + when(mTrustManager.isInSignificantPlace()).thenReturn(true); + + final BiometricSensor sensor = getFaceSensor(); + BiometricSensor fingerprintSensor = getFingerprintSensor(); + final PromptInfo promptInfo = new PromptInfo(); + promptInfo.setAuthenticators(BiometricManager.Authenticators.IDENTITY_CHECK + | BiometricManager.Authenticators.BIOMETRIC_STRONG); + final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager, + mSettingObserver, List.of(sensor, fingerprintSensor), USER_ID, promptInfo, + TEST_PACKAGE_NAME, false /* checkDevicePolicyManager */, mContext, + mBiometricCameraManager, mUserManager); + + assertThat(preAuthInfo.eligibleSensors).hasSize(1); + assertThat(preAuthInfo.eligibleSensors.get(0).modality).isEqualTo(TYPE_FINGERPRINT); + assertThat(preAuthInfo.ineligibleSensors).hasSize(1); + assertThat(preAuthInfo.ineligibleSensors.get(0).first.modality).isEqualTo(TYPE_FACE); + assertThat(preAuthInfo.ineligibleSensors.get(0).second) + .isEqualTo(BIOMETRIC_NOT_ENABLED_FOR_APPS); + } + + @Test + @RequiresFlagsEnabled(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION) + public void testBiometricsEnabledForApps_fingerprintDisabled_faceEnabled() + throws Exception { + when(mSettingObserver.getEnabledForApps(USER_ID, TYPE_FINGERPRINT)).thenReturn(false); + when(mSettingObserver.getEnabledForApps(USER_ID, TYPE_FACE)).thenReturn(true); + when(mTrustManager.isInSignificantPlace()).thenReturn(true); + + final BiometricSensor sensor = getFaceSensor(); + BiometricSensor fingerprintSensor = getFingerprintSensor(); + final PromptInfo promptInfo = new PromptInfo(); + promptInfo.setAuthenticators(BiometricManager.Authenticators.IDENTITY_CHECK + | BiometricManager.Authenticators.BIOMETRIC_STRONG); + final PreAuthInfo preAuthInfo = PreAuthInfo.create(mTrustManager, mDevicePolicyManager, + mSettingObserver, List.of(sensor, fingerprintSensor), USER_ID, promptInfo, + TEST_PACKAGE_NAME, false /* checkDevicePolicyManager */, mContext, + mBiometricCameraManager, mUserManager); + + assertThat(preAuthInfo.eligibleSensors).hasSize(1); + assertThat(preAuthInfo.eligibleSensors.get(0).modality).isEqualTo(TYPE_FACE); + assertThat(preAuthInfo.ineligibleSensors).hasSize(1); + assertThat(preAuthInfo.ineligibleSensors.get(0).first.modality).isEqualTo(TYPE_FINGERPRINT); + assertThat(preAuthInfo.ineligibleSensors.get(0).second) + .isEqualTo(BIOMETRIC_NOT_ENABLED_FOR_APPS); + } + private BiometricSensor getFingerprintSensor() { BiometricSensor sensor = new BiometricSensor(mContext, SENSOR_ID_FINGERPRINT, TYPE_FINGERPRINT, BiometricManager.Authenticators.BIOMETRIC_STRONG, |