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

Commit 31cdbebc authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] Pick up on non-secure auth method changes." into main

parents b47e87c5 1c80a98d
Loading
Loading
Loading
Loading
+57 −0
Original line number Diff line number Diff line
@@ -1559,6 +1559,63 @@ class SceneContainerStartableTest : SysuiTestCase() {
            verify(dismissCallback).onDismissCancelled()
        }

    @Test
    fun refreshLockscreenEnabled() =
        testScope.runTest {
            val transitionState =
                prepareState(
                    isDeviceUnlocked = true,
                    initialSceneKey = Scenes.Gone,
                )
            underTest.start()
            val isLockscreenEnabled by
                collectLastValue(kosmos.deviceEntryInteractor.isLockscreenEnabled)
            assertThat(isLockscreenEnabled).isTrue()

            kosmos.fakeDeviceEntryRepository.setPendingLockscreenEnabled(false)
            runCurrent()
            // Pending value didn't propagate yet.
            assertThat(isLockscreenEnabled).isTrue()

            // Starting a transition to Lockscreen should refresh the value, causing the pending
            // value
            // to propagate to the real flow:
            transitionState.value =
                ObservableTransitionState.Transition(
                    fromScene = Scenes.Gone,
                    toScene = Scenes.Lockscreen,
                    currentScene = flowOf(Scenes.Gone),
                    progress = flowOf(0.1f),
                    isInitiatedByUserInput = false,
                    isUserInputOngoing = flowOf(false),
                )
            runCurrent()
            assertThat(isLockscreenEnabled).isFalse()

            kosmos.fakeDeviceEntryRepository.setPendingLockscreenEnabled(true)
            runCurrent()
            // Pending value didn't propagate yet.
            assertThat(isLockscreenEnabled).isFalse()
            transitionState.value = ObservableTransitionState.Idle(Scenes.Gone)
            runCurrent()
            assertThat(isLockscreenEnabled).isFalse()

            // Starting another transition to Lockscreen should refresh the value, causing the
            // pending
            // value to propagate to the real flow:
            transitionState.value =
                ObservableTransitionState.Transition(
                    fromScene = Scenes.Gone,
                    toScene = Scenes.Lockscreen,
                    currentScene = flowOf(Scenes.Gone),
                    progress = flowOf(0.1f),
                    isInitiatedByUserInput = false,
                    isUserInputOngoing = flowOf(false),
                )
            runCurrent()
            assertThat(isLockscreenEnabled).isTrue()
        }

    private fun TestScope.emulateSceneTransition(
        transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
        toScene: SceneKey,
+21 −7
Original line number Diff line number Diff line
@@ -13,8 +13,10 @@ import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.withContext

@@ -25,7 +27,7 @@ interface DeviceEntryRepository {
     * chosen any secure authentication method and even if they set the lockscreen to be dismissed
     * when the user swipes on it.
     */
    suspend fun isLockscreenEnabled(): Boolean
    val isLockscreenEnabled: StateFlow<Boolean>

    /**
     * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically
@@ -39,6 +41,13 @@ interface DeviceEntryRepository {
     * the lockscreen.
     */
    val isBypassEnabled: StateFlow<Boolean>

    /**
     * Whether the lockscreen is enabled for the current user. This is `true` whenever the user has
     * chosen any secure authentication method and even if they set the lockscreen to be dismissed
     * when the user swipes on it.
     */
    suspend fun isLockscreenEnabled(): Boolean
}

/** Encapsulates application state for device entry. */
@@ -53,12 +62,8 @@ constructor(
    private val keyguardBypassController: KeyguardBypassController,
) : DeviceEntryRepository {

    override suspend fun isLockscreenEnabled(): Boolean {
        return withContext(backgroundDispatcher) {
            val selectedUserId = userRepository.getSelectedUserInfo().id
            !lockPatternUtils.isLockScreenDisabled(selectedUserId)
        }
    }
    private val _isLockscreenEnabled = MutableStateFlow(true)
    override val isLockscreenEnabled: StateFlow<Boolean> = _isLockscreenEnabled.asStateFlow()

    override val isBypassEnabled: StateFlow<Boolean> =
        conflatedCallbackFlow {
@@ -78,6 +83,15 @@ constructor(
                SharingStarted.Eagerly,
                initialValue = keyguardBypassController.bypassEnabled,
            )

    override suspend fun isLockscreenEnabled(): Boolean {
        return withContext(backgroundDispatcher) {
            val selectedUserId = userRepository.getSelectedUserInfo().id
            val isEnabled = !lockPatternUtils.isLockScreenDisabled(selectedUserId)
            _isLockscreenEnabled.value = isEnabled
            isEnabled
        }
    }
}

@Module
+21 −4
Original line number Diff line number Diff line
@@ -28,12 +28,14 @@ import com.android.systemui.utils.coroutines.flow.mapLatestConflated
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

@@ -101,6 +103,10 @@ constructor(
                initialValue = false,
            )

    val isLockscreenEnabled: Flow<Boolean> by lazy {
        repository.isLockscreenEnabled.onStart { refreshLockscreenEnabled() }
    }

    /**
     * Whether it's currently possible to swipe up to enter the device without requiring
     * authentication or when the device is already authenticated using a passive authentication
@@ -115,14 +121,14 @@ constructor(
     */
    val canSwipeToEnter: StateFlow<Boolean?> =
        combine(
                // This is true when the user has chosen to show the lockscreen but has not made it
                // secure.
                authenticationInteractor.authenticationMethod.map {
                    it == AuthenticationMethodModel.None && repository.isLockscreenEnabled()
                    it == AuthenticationMethodModel.None
                },
                isLockscreenEnabled,
                deviceUnlockedInteractor.deviceUnlockStatus,
                isDeviceEntered
            ) { isSwipeAuthMethod, deviceUnlockStatus, isDeviceEntered ->
            ) { isNoneAuthMethod, isLockscreenEnabled, deviceUnlockStatus, isDeviceEntered ->
                val isSwipeAuthMethod = isNoneAuthMethod && isLockscreenEnabled
                (isSwipeAuthMethod ||
                    (deviceUnlockStatus.isUnlocked &&
                        deviceUnlockStatus.deviceUnlockSource?.dismissesLockscreen == false)) &&
@@ -185,6 +191,17 @@ constructor(
        return repository.isLockscreenEnabled()
    }

    /**
     * Forces a refresh of the value of [isLockscreenEnabled] such that the flow emits the latest
     * value.
     *
     * Without calling this method, the flow will have a stale value unless the collector is removed
     * and re-added.
     */
    suspend fun refreshLockscreenEnabled() {
        isLockscreenEnabled()
    }

    /**
     * Whether lockscreen bypass is enabled. When enabled, the lockscreen will be automatically
     * dismissed once the authentication challenge is completed. For example, completing a biometric
+3 −3
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode.Companion.isWakeAndUnlock
@@ -59,7 +59,7 @@ constructor(
    private val communalInteractor: CommunalInteractor,
    private val communalSceneInteractor: CommunalSceneInteractor,
    keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
    val deviceEntryRepository: DeviceEntryRepository,
    val deviceEntryInteractor: DeviceEntryInteractor,
    private val wakeToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor,
    private val dreamManager: DreamManager,
) :
@@ -146,7 +146,7 @@ constructor(
                        isIdleOnCommunal,
                        canTransitionToGoneOnWake,
                        primaryBouncerShowing) ->
                    if (!deviceEntryRepository.isLockscreenEnabled()) {
                    if (!deviceEntryInteractor.isLockscreenEnabled()) {
                        if (SceneContainerFlag.isEnabled) {
                            // TODO(b/336576536): Check if adaptation for scene framework is needed
                        } else {
+19 −0
Original line number Diff line number Diff line
@@ -149,6 +149,7 @@ constructor(
            resetShadeSessions()
            handleKeyguardEnabledness()
            notifyKeyguardDismissCallbacks()
            refreshLockscreenEnabled()
        } else {
            sceneLogger.logFrameworkEnabled(
                isEnabled = false,
@@ -735,4 +736,22 @@ constructor(
            }
        }
    }

    /**
     * Keeps the value of [DeviceEntryInteractor.isLockscreenEnabled] fresh.
     *
     * This is needed because that value is sourced from a non-observable data source
     * (`LockPatternUtils`, which doesn't expose a listener or callback for this value). Therefore,
     * every time a transition to the `Lockscreen` scene is started, the value is re-fetched and
     * cached.
     */
    private fun refreshLockscreenEnabled() {
        applicationScope.launch {
            sceneInteractor.transitionState
                .map { it.isTransitioning(to = Scenes.Lockscreen) }
                .distinctUntilChanged()
                .filter { it }
                .collectLatest { deviceEntryInteractor.refreshLockscreenEnabled() }
        }
    }
}
Loading