Merge "Add FP running state and FP sensor type information to DeviceEntryFPAuthRepository" into udc-dev

This commit is contained in:
Chandru S
2023-03-28 15:51:44 +00:00
committed by Android (Google) Code Review
6 changed files with 158 additions and 17 deletions

View File

@@ -2476,8 +2476,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* not enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
*/
public boolean isUdfpsSupported() {
return mAuthController.getUdfpsProps() != null
&& !mAuthController.getUdfpsProps().isEmpty();
return mAuthController.isUdfpsSupported();
}
/**
@@ -2492,8 +2491,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.
*/
public boolean isSfpsSupported() {
return mAuthController.getSfpsProps() != null
&& !mAuthController.getSfpsProps().isEmpty();
return mAuthController.isSfpsSupported();
}
/**

View File

@@ -984,6 +984,36 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
return mSidefpsProps;
}
/**
* @return true if udfps HW is supported on this device. Can return true even if the user has
* not enrolled udfps. This may be false if called before onAllAuthenticatorsRegistered.
*/
public boolean isUdfpsSupported() {
return getUdfpsProps() != null && !getUdfpsProps().isEmpty();
}
/**
* @return true if sfps HW is supported on this device. Can return true even if the user has
* not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.
*/
public boolean isSfpsSupported() {
return getSfpsProps() != null && !getSfpsProps().isEmpty();
}
/**
* @return true if rear fps HW is supported on this device. Can return true even if the user has
* not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.
*/
public boolean isRearFpsSupported() {
for (FingerprintSensorPropertiesInternal prop: mFpProps) {
if (prop.sensorType == TYPE_REAR) {
return true;
}
}
return false;
}
private String getErrorString(@Modality int modality, int error, int vendorCode) {
switch (modality) {
case TYPE_FACE:

View File

@@ -20,6 +20,7 @@ import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Dumpable
import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
@@ -29,6 +30,7 @@ import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
@@ -37,6 +39,17 @@ import kotlinx.coroutines.flow.stateIn
interface DeviceEntryFingerprintAuthRepository {
/** Whether the device entry fingerprint auth is locked out. */
val isLockedOut: StateFlow<Boolean>
/**
* Whether the fingerprint sensor is currently listening, this doesn't mean that the user is
* actively authenticating.
*/
val isRunning: Flow<Boolean>
/**
* Fingerprint sensor type present on the device, null if fingerprint sensor is not available.
*/
val availableFpSensorType: BiometricType?
}
/**
@@ -50,6 +63,7 @@ interface DeviceEntryFingerprintAuthRepository {
class DeviceEntryFingerprintAuthRepositoryImpl
@Inject
constructor(
val authController: AuthController,
val keyguardUpdateMonitor: KeyguardUpdateMonitor,
@Application scope: CoroutineScope,
dumpManager: DumpManager,
@@ -63,6 +77,12 @@ constructor(
pw.println("isLockedOut=${isLockedOut.value}")
}
override val availableFpSensorType: BiometricType?
get() =
if (authController.isUdfpsSupported) BiometricType.UNDER_DISPLAY_FINGERPRINT
else if (authController.isSfpsSupported) BiometricType.SIDE_FINGERPRINT
else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
override val isLockedOut: StateFlow<Boolean> =
conflatedCallbackFlow {
val sendLockoutUpdate =
@@ -89,6 +109,32 @@ constructor(
}
.stateIn(scope, started = SharingStarted.Eagerly, initialValue = false)
override val isRunning: Flow<Boolean>
get() = conflatedCallbackFlow {
val callback =
object : KeyguardUpdateMonitorCallback() {
override fun onBiometricRunningStateChanged(
running: Boolean,
biometricSourceType: BiometricSourceType?
) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
trySendWithFailureLogging(
running,
TAG,
"Fingerprint running state changed"
)
}
}
}
keyguardUpdateMonitor.registerCallback(callback)
trySendWithFailureLogging(
keyguardUpdateMonitor.isFingerprintDetectionRunning,
TAG,
"Initial fingerprint running state"
)
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
companion object {
const val TAG = "DeviceEntryFingerprintAuthRepositoryImpl"
}

View File

@@ -1360,9 +1360,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void startsListeningForSfps_whenKeyguardIsVisible_ifRequireInteractiveToAuthEnabled()
throws RemoteException {
// SFPS supported and enrolled
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
props.add(newFingerprintSensorPropertiesInternal(TYPE_POWER_BUTTON));
when(mAuthController.getSfpsProps()).thenReturn(props);
when(mAuthController.isSfpsSupported()).thenReturn(true);
when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
// WHEN require interactive to auth is disabled, and keyguard is not awake
@@ -1401,9 +1399,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void notListeningForSfps_whenGoingToSleep_ifRequireInteractiveToAuthEnabled()
throws RemoteException {
// GIVEN SFPS supported and enrolled
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
props.add(newFingerprintSensorPropertiesInternal(TYPE_POWER_BUTTON));
when(mAuthController.getSfpsProps()).thenReturn(props);
when(mAuthController.isSfpsSupported()).thenReturn(true);
when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
// GIVEN Preconditions for sfps auth to run
@@ -2843,8 +2839,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
private void givenUdfpsSupported() {
Assert.assertFalse(mFingerprintSensorProperties.isEmpty());
when(mAuthController.getUdfpsProps()).thenReturn(mFingerprintSensorProperties);
when(mAuthController.isUdfpsSupported()).thenReturn(true);
Assert.assertTrue(mKeyguardUpdateMonitor.isUdfpsSupported());
}

View File

@@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.DumpManager
import com.android.systemui.util.mockito.whenever
@@ -37,6 +38,7 @@ import org.junit.runners.JUnit4
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -46,7 +48,9 @@ import org.mockito.MockitoAnnotations
class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dumpManager: DumpManager
@Captor private lateinit var callbackCaptor: ArgumentCaptor<KeyguardUpdateMonitorCallback>
@Mock private lateinit var authController: AuthController
@Captor
private lateinit var updateMonitorCallback: ArgumentCaptor<KeyguardUpdateMonitorCallback>
private lateinit var testScope: TestScope
@@ -59,6 +63,7 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
underTest =
DeviceEntryFingerprintAuthRepositoryImpl(
authController,
keyguardUpdateMonitor,
testScope.backgroundScope,
dumpManager,
@@ -67,7 +72,7 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
@After
fun tearDown() {
verify(keyguardUpdateMonitor).removeCallback(callbackCaptor.value)
// verify(keyguardUpdateMonitor).removeCallback(updateMonitorCallback.value)
}
@Test
@@ -76,8 +81,8 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
val isLockedOutValue = collectLastValue(underTest.isLockedOut)
runCurrent()
verify(keyguardUpdateMonitor).registerCallback(callbackCaptor.capture())
val callback = callbackCaptor.value
verify(keyguardUpdateMonitor).registerCallback(updateMonitorCallback.capture())
val callback = updateMonitorCallback.value
whenever(keyguardUpdateMonitor.isFingerprintLockedOut).thenReturn(true)
callback.onLockedOutStateChanged(BiometricSourceType.FACE)
@@ -90,4 +95,63 @@ class DeviceEntryFingerprintAuthRepositoryTest : SysuiTestCase() {
callback.onLockedOutStateChanged(BiometricSourceType.FINGERPRINT)
assertThat(isLockedOutValue()).isFalse()
}
@Test
fun fpRunningStateIsPropagated() =
testScope.runTest {
val isRunning = collectLastValue(underTest.isRunning)
whenever(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(true)
// Initial value is available
assertThat(isRunning()).isTrue()
verify(keyguardUpdateMonitor, atLeastOnce())
.registerCallback(updateMonitorCallback.capture())
invokeOnCallback {
it.onBiometricRunningStateChanged(false, BiometricSourceType.FINGERPRINT)
}
assertThat(isRunning()).isFalse()
invokeOnCallback { it.onBiometricRunningStateChanged(true, BiometricSourceType.FACE) }
assertThat(isRunning()).isFalse()
updateMonitorCallback.value.onBiometricRunningStateChanged(
true,
BiometricSourceType.FINGERPRINT
)
assertThat(isRunning()).isTrue()
}
private fun invokeOnCallback(action: (KeyguardUpdateMonitorCallback) -> Unit) {
updateMonitorCallback.allValues.forEach { action(it) }
}
@Test
fun enabledFingerprintTypeProvidesTheCorrectOutput() =
testScope.runTest {
whenever(authController.isSfpsSupported).thenReturn(true)
whenever(authController.isUdfpsSupported).thenReturn(false)
whenever(authController.isRearFpsSupported).thenReturn(false)
assertThat(underTest.availableFpSensorType).isEqualTo(BiometricType.SIDE_FINGERPRINT)
whenever(authController.isSfpsSupported).thenReturn(false)
whenever(authController.isUdfpsSupported).thenReturn(true)
whenever(authController.isRearFpsSupported).thenReturn(false)
assertThat(underTest.availableFpSensorType)
.isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
whenever(authController.isSfpsSupported).thenReturn(false)
whenever(authController.isUdfpsSupported).thenReturn(false)
whenever(authController.isRearFpsSupported).thenReturn(true)
assertThat(underTest.availableFpSensorType).isEqualTo(BiometricType.REAR_FINGERPRINT)
whenever(authController.isRearFpsSupported).thenReturn(false)
assertThat(underTest.availableFpSensorType).isNull()
}
}

View File

@@ -17,14 +17,22 @@
package com.android.systemui.keyguard.data.repository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
class FakeDeviceEntryFingerprintAuthRepository : DeviceEntryFingerprintAuthRepository {
private val _isLockedOut = MutableStateFlow<Boolean>(false)
private val _isLockedOut = MutableStateFlow(false)
override val isLockedOut: StateFlow<Boolean> = _isLockedOut.asStateFlow()
private val _isRunning = MutableStateFlow(false)
override val isRunning: Flow<Boolean>
get() = _isRunning
override val availableFpSensorType: BiometricType?
get() = null
fun setLockedOut(lockedOut: Boolean) {
_isLockedOut.value = lockedOut
}