Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d948e9d5 authored by Chandru S's avatar Chandru S Committed by Automerger Merge Worker
Browse files

Merge "Provide isUserInLockdown through BiometricSettingsRepository" into udc-dev am: 59702143

parents 2dc25c2a 59702143
Loading
Loading
Loading
Loading
+72 −37
Original line number Diff line number Diff line
@@ -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,35 +267,11 @@ 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(
        strongAuthTracker.isStrongBiometricAllowed.stateIn(
            scope,
                started = SharingStarted.Eagerly,
                initialValue =
                    lockPatternUtils.isBiometricAllowedForUser(
            SharingStarted.Eagerly,
            strongAuthTracker.isBiometricAllowedForUser(
                true,
                userRepository.getSelectedUserInfo().id
            )
        )
@@ -300,9 +292,44 @@ constructor(
                        userRepository.getSelectedUserInfo().id
                    )
            )
}

private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
    LockPatternUtils.StrongAuthTracker(context) {

    private val _authFlags =
        MutableStateFlow(
            StrongAuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId))
        )

    companion object {
        private const val TAG = "BiometricsRepositoryImpl"
    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
}
+37 −13
Original line number Diff line number Diff line
@@ -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)
    }
+4 −0
Original line number Diff line number Diff line
@@ -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
    }