Provide isUserInLockdown through BiometricSettingsRepository
Since LockPatternUtils.registerStrongAuthTracker is a binder based listener,
this CL changes BiometricSettingsRepository#isStrongBiometricAllowed to rely on the same listener we register for isUserInLockdown
Bug: 262838215
Test: atest BiometricSettingsRepositoryTest
(cherry picked from https://googleplex-android-review.googlesource.com/q/commit:231b255ae166743b1834bf3c105f0b88e39cdcb3)
Merged-In: If9cb20ce12d2aa6ace5a1a5ccce2b887bdfa6e44
Change-Id: If9cb20ce12d2aa6ace5a1a5ccce2b887bdfa6e44
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 84abf57..d5129a6 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
@@ -22,10 +22,10 @@
import android.content.IntentFilter
import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback
-import android.os.Looper
import android.os.UserHandle
import android.util.Log
import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.biometrics.AuthController
@@ -35,8 +35,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.TAG
import com.android.systemui.keyguard.shared.model.DevicePosture
import com.android.systemui.user.data.repository.UserRepository
import java.io.PrintWriter
@@ -45,10 +45,12 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
@@ -93,8 +95,16 @@
* restricted to specific postures using [R.integer.config_face_auth_supported_posture]
*/
val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
+
+ /**
+ * Whether the user manually locked down the device. This doesn't include device policy manager
+ * lockdown.
+ */
+ val isCurrentUserInLockdown: Flow<Boolean>
}
+const val TAG = "BiometricsRepositoryImpl"
+
@SysUISingleton
class BiometricSettingsRepositoryImpl
@Inject
@@ -103,19 +113,25 @@
lockPatternUtils: LockPatternUtils,
broadcastDispatcher: BroadcastDispatcher,
authController: AuthController,
- userRepository: UserRepository,
+ private val userRepository: UserRepository,
devicePolicyManager: DevicePolicyManager,
@Application scope: CoroutineScope,
@Background backgroundDispatcher: CoroutineDispatcher,
biometricManager: BiometricManager?,
- @Main looper: Looper,
devicePostureRepository: DevicePostureRepository,
dumpManager: DumpManager,
) : BiometricSettingsRepository, Dumpable {
override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
+ private val strongAuthTracker = StrongAuthTracker(userRepository, context)
+
+ override val isCurrentUserInLockdown: Flow<Boolean> =
+ strongAuthTracker.currentUserAuthFlags.map { it.isInUserLockdown }
+
init {
+ Log.d(TAG, "Registering StrongAuthTracker")
+ lockPatternUtils.registerStrongAuthTracker(strongAuthTracker)
dumpManager.registerDumpable(this)
val configFaceAuthSupportedPosture =
DevicePosture.toPosture(
@@ -251,38 +267,14 @@
.stateIn(scope, SharingStarted.Eagerly, false)
override val isStrongBiometricAllowed: StateFlow<Boolean> =
- selectedUserId
- .flatMapLatest { currUserId ->
- conflatedCallbackFlow {
- val callback =
- object : LockPatternUtils.StrongAuthTracker(context, looper) {
- override fun onStrongAuthRequiredChanged(userId: Int) {
- if (currUserId != userId) {
- return
- }
-
- trySendWithFailureLogging(
- isBiometricAllowedForUser(true, currUserId),
- TAG
- )
- }
-
- override fun onIsNonStrongBiometricAllowedChanged(userId: Int) {
- // no-op
- }
- }
- lockPatternUtils.registerStrongAuthTracker(callback)
- awaitClose { lockPatternUtils.unregisterStrongAuthTracker(callback) }
- }
- }
- .stateIn(
- scope,
- started = SharingStarted.Eagerly,
- initialValue =
- lockPatternUtils.isBiometricAllowedForUser(
- userRepository.getSelectedUserInfo().id
- )
+ strongAuthTracker.isStrongBiometricAllowed.stateIn(
+ scope,
+ SharingStarted.Eagerly,
+ strongAuthTracker.isBiometricAllowedForUser(
+ true,
+ userRepository.getSelectedUserInfo().id
)
+ )
override val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> =
selectedUserId
@@ -300,9 +292,44 @@
userRepository.getSelectedUserInfo().id
)
)
+}
- companion object {
- private const val TAG = "BiometricsRepositoryImpl"
+private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
+ LockPatternUtils.StrongAuthTracker(context) {
+
+ private val _authFlags =
+ MutableStateFlow(
+ StrongAuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId))
+ )
+
+ val currentUserAuthFlags: Flow<StrongAuthenticationFlags> =
+ userRepository.selectedUserInfo
+ .map { it.id }
+ .distinctUntilChanged()
+ .flatMapLatest { currUserId ->
+ _authFlags
+ .filter { it.userId == currUserId }
+ .onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
+ .onStart {
+ emit(
+ StrongAuthenticationFlags(
+ currentUserId,
+ getStrongAuthForUser(currentUserId)
+ )
+ )
+ }
+ }
+
+ val isStrongBiometricAllowed: Flow<Boolean> =
+ currentUserAuthFlags.map { isBiometricAllowedForUser(true, it.userId) }
+
+ private val currentUserId
+ get() = userRepository.getSelectedUserInfo().id
+
+ override fun onStrongAuthRequiredChanged(userId: Int) {
+ val newFlags = getStrongAuthForUser(userId)
+ _authFlags.value = StrongAuthenticationFlags(userId, newFlags)
+ Log.d(TAG, "onStrongAuthRequiredChanged for userId: $userId, flag value: $newFlags")
}
}
@@ -314,3 +341,11 @@
private fun DevicePolicyManager.isNotActive(userId: Int, policy: Int): Boolean =
(getKeyguardDisabledFeatures(null, userId) and policy) == 0
+
+private data class StrongAuthenticationFlags(val userId: Int, val flag: Int) {
+ val isInUserLockdown = containsFlag(flag, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN)
+}
+
+private fun containsFlag(haystack: Int, needle: Int): Boolean {
+ return haystack and needle != 0
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index 5dc04f7..fb7d379 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -30,6 +30,8 @@
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
@@ -42,7 +44,6 @@
import com.android.systemui.keyguard.shared.model.DevicePosture
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -78,6 +79,8 @@
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
@Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var biometricManager: BiometricManager
+ @Captor
+ private lateinit var strongAuthTracker: ArgumentCaptor<LockPatternUtils.StrongAuthTracker>
@Captor private lateinit var authControllerCallback: ArgumentCaptor<AuthController.Callback>
@Captor
private lateinit var biometricManagerCallback:
@@ -112,12 +115,12 @@
devicePolicyManager = devicePolicyManager,
scope = testScope.backgroundScope,
backgroundDispatcher = testDispatcher,
- looper = testableLooper!!.looper,
- dumpManager = dumpManager,
biometricManager = biometricManager,
devicePostureRepository = devicePostureRepository,
+ dumpManager = dumpManager,
)
testScope.runCurrent()
+ verify(lockPatternUtils).registerStrongAuthTracker(strongAuthTracker.capture())
}
@Test
@@ -147,21 +150,18 @@
val strongBiometricAllowed = collectLastValue(underTest.isStrongBiometricAllowed)
runCurrent()
- val captor = argumentCaptor<LockPatternUtils.StrongAuthTracker>()
- verify(lockPatternUtils).registerStrongAuthTracker(captor.capture())
-
- captor.value.stub.onStrongAuthRequiredChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
- testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
+ onStrongAuthChanged(STRONG_AUTH_NOT_REQUIRED, PRIMARY_USER_ID)
assertThat(strongBiometricAllowed()).isTrue()
- captor.value.stub.onStrongAuthRequiredChanged(
- STRONG_AUTH_REQUIRED_AFTER_BOOT,
- PRIMARY_USER_ID
- )
- testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
+ onStrongAuthChanged(STRONG_AUTH_REQUIRED_AFTER_BOOT, PRIMARY_USER_ID)
assertThat(strongBiometricAllowed()).isFalse()
}
+ private fun onStrongAuthChanged(flags: Int, userId: Int) {
+ strongAuthTracker.value.stub.onStrongAuthRequiredChanged(flags, userId)
+ testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
+ }
+
@Test
fun fingerprintDisabledByDpmChange() =
testScope.runTest {
@@ -351,6 +351,30 @@
assertThat(isFaceAuthSupported()).isTrue()
}
+ @Test
+ fun userInLockdownUsesStrongAuthFlagsToDetermineValue() =
+ testScope.runTest {
+ createBiometricSettingsRepository()
+
+ val isUserInLockdown = collectLastValue(underTest.isCurrentUserInLockdown)
+ // has default value.
+ assertThat(isUserInLockdown()).isFalse()
+
+ // change strong auth flags for another user.
+ // Combine with one more flag to check if we do the bitwise and
+ val inLockdown =
+ STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN or STRONG_AUTH_REQUIRED_AFTER_TIMEOUT
+ onStrongAuthChanged(inLockdown, ANOTHER_USER_ID)
+
+ // Still false.
+ assertThat(isUserInLockdown()).isFalse()
+
+ // change strong auth flags for current user.
+ onStrongAuthChanged(inLockdown, PRIMARY_USER_ID)
+
+ assertThat(isUserInLockdown()).isTrue()
+ }
+
private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) {
authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index d4b1701..d8b3270 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -46,6 +46,10 @@
override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
get() = flowOf(true)
+ private val _isCurrentUserInLockdown = MutableStateFlow(false)
+ override val isCurrentUserInLockdown: Flow<Boolean>
+ get() = _isCurrentUserInLockdown
+
fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) {
_isFingerprintEnrolled.value = isFingerprintEnrolled
}