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
This commit is contained in:
committed by
Cherrypicker Worker
parent
8246587377
commit
58d59134a0
@@ -22,10 +22,10 @@ import android.content.Context
|
||||
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.common.coroutine.ConflatedCallbackFlow.conflatedCall
|
||||
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.CoroutineDispatcher
|
||||
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 @@ interface BiometricSettingsRepository {
|
||||
* 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 @@ constructor(
|
||||
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 @@ constructor(
|
||||
.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 @@ constructor(
|
||||
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.isFingerprintDisabled(userId: Int): Boolean =
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ import androidx.test.filters.SmallTest
|
||||
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.data.repository.BiometricType.UNDER_DISPLAY
|
||||
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 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
|
||||
@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 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
|
||||
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 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
|
||||
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 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -46,6 +46,10 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository {
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user