Split enabledForApp & enabledOnKeyguard settings for FP and Face
Make fingerprint & face have their own set of setting keys of
enablaedForApps and enabledForKeyguard.
Bug: 370940762
Test: atest AuthSessionTest
BiometricServiceTest
PreAuthInfoTest
KeyguardUpdateMonitorTest
BiometricSettingsRepositoryTest
SettingsBackupTest
Flag: com.android.settings.flags.biometrics_onboarding_education
Change-Id: Ia7156828d0bce2bbf0389431d3d5299f646bbffe
diff --git a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
index 1268658..cf25b7e 100644
--- a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
@@ -20,5 +20,5 @@
* @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 73d1e17..7d067fb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -11346,17 +11346,54 @@
/**
* 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 BIOMETRIC_APP_ENABLED = "biometric_app_enabled";
+ 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.
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 7b4a2ca..7aef657 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 @@
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 6681c014..43ceda7 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -455,5 +455,9 @@
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 a935aac..67a9064 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -521,6 +521,7 @@
"androidx.compose.animation_animation-graphics",
"androidx.lifecycle_lifecycle-viewmodel-compose",
"kairos",
+ "aconfig_settings_flags_lib",
],
libs: [
"keepanno-annotations",
@@ -702,6 +703,7 @@
"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 69fb03d..4e8a2a3 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_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.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 @@
fun fingerprintEnrollmentChange() =
testScope.runTest {
createBiometricSettingsRepository()
- biometricsAreEnabledBySettings()
+ biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT)
val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
runCurrent()
@@ -173,7 +179,7 @@
fun fingerprintEnabledStateChange() =
testScope.runTest {
createBiometricSettingsRepository()
- biometricsAreEnabledBySettings()
+ biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT)
val fingerprintAllowed = collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
runCurrent()
@@ -183,21 +189,59 @@
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
fun strongBiometricAllowedChange() =
testScope.runTest {
fingerprintIsEnrolled()
doNotDisableKeyguardAuthFeatures()
createBiometricSettingsRepository()
- biometricsAreEnabledBySettings()
+ biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT)
val strongBiometricAllowed by
collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
@@ -220,7 +264,7 @@
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 @@
faceAuthIsEnrolled()
createBiometricSettingsRepository()
doNotDisableKeyguardAuthFeatures()
- biometricsAreEnabledBySettings()
+ biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FACE)
runCurrent()
val convenienceBiometricAllowed by
@@ -315,7 +359,7 @@
testScope.runTest {
fingerprintIsEnrolled(PRIMARY_USER_ID)
createBiometricSettingsRepository()
- biometricsAreEnabledBySettings()
+ biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT)
val fingerprintEnabledByDevicePolicy =
collectLastValue(underTest.isFingerprintEnrolledAndEnabled)
@@ -341,7 +385,7 @@
createBiometricSettingsRepository()
val faceAuthAllowed = collectLastValue(underTest.isFaceAuthEnrolledAndEnabled)
- biometricsAreEnabledBySettings()
+ biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FACE)
doNotDisableKeyguardAuthFeatures(PRIMARY_USER_ID)
@@ -376,16 +420,22 @@
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 @@
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 @@
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 @@
faceAuthIsEnrolled()
createBiometricSettingsRepository()
- biometricsAreEnabledBySettings()
+ biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FACE)
doNotDisableKeyguardAuthFeatures()
mobileConnectionsRepository.fake.isAnySimSecure.value = false
runCurrent()
@@ -485,7 +535,7 @@
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 @@
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 @@
.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 @@
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 @@
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 @@
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 @@
fun fpAuthCurrentlyAllowed_dependsOnNonStrongAuthBiometricSetting_ifFpIsNotStrong() =
testScope.runTest {
createBiometricSettingsRepository()
- biometricsAreEnabledBySettings()
+ biometricsAreEnabledBySettings(PRIMARY_USER_ID, TYPE_FINGERPRINT)
val isFingerprintCurrentlyAllowed by
collectLastValue(underTest.isFingerprintAuthCurrentlyAllowed)
@@ -779,7 +829,7 @@
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 7d291c3..61038ef 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -437,8 +437,12 @@
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 ab8cc71..4e7de5d 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.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 @@
) : 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 @@
}
}
- 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 @@
.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 @@
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 839a2bd..2645811 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 @@
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 @@
}
@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 @@
}
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 42385fc..bb49337 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -230,6 +230,7 @@
"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 b365ef7..15b1f22 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.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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 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 @@
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 @@
}
}
- 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 @@
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 @@
@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 6ed1ac85..c739118 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 @@
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 4ef37b9..8b9def9 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 @@
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 @@
}
});
- 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 88829c1..acca4cc 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 @@
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 @@
// 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 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 @@
@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 @@
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 @@
}
@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,10 @@
mBiometricService = new BiometricService(mContext, mInjector, mBiometricHandlerProvider);
when(mUserManager.getAliveUsers()).thenReturn(aliveUsers);
- when(mBiometricService.mSettingObserver.getEnabledOnKeyguard(userInfo1.id))
- .thenReturn(true);
- when(mBiometricService.mSettingObserver.getEnabledOnKeyguard(userInfo2.id))
- .thenReturn(false);
+ 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);
@@ -1968,8 +1975,42 @@
waitForIdle();
verify(callback).asBinder();
- verify(callback).onChanged(true, userInfo1.id);
- verify(callback).onChanged(false, userInfo2.id);
+ 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(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);
+
+ waitForIdle();
+
+ verify(callback).asBinder();
+ 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 @@
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 @@
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 @@
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 f6f831f..f8cf21d 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_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 @@
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 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 @@
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,