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

Commit 42f1f762 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Adds support for trust agents

When the environment is trusted, the device shouldn't become locked when
it goes to sleep or starts dreaming. At least not until the environment
becomes untrusted again or a more explicit request to lock happens (for
example, a lock down).

Fix: 392181543
Test: unit tests added
Test: manually verified, using bluetooth headphones that have been set
up as a trusted device in the "Extend unlock" feature in settings, that (1) the device
remains unlocked while the headphones are present for up to the
timeout/limit (4 hours normally but locally changed to 60 seconds by
editing TRUST_TIMEOUT_IN_MILLIS in TrustManagerService). Also verified
that bringing up the power menu makes the environment untrusted and
that disconnecting the trusted device also does.
Flag: com.android.systemui.scene_container

Change-Id: I81a5e0fa2ac3b5b6c858274d4af478efb8daaeb0
parent f1dd746c
Loading
Loading
Loading
Loading
+57 −1
Original line number Diff line number Diff line
@@ -570,7 +570,7 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
            unlockDevice()
            assertThat(isUnlocked).isTrue()

            underTest.lockNow()
            underTest.lockNow("test")
            runCurrent()

            assertThat(isUnlocked).isFalse()
@@ -597,6 +597,62 @@ class DeviceUnlockedInteractorTest : SysuiTestCase() {
            assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
        }

    @Test
    fun deviceUnlockStatus_staysUnlocked_whenDeviceGoesToSleep_whileIsTrusted() =
        testScope.runTest {
            setLockAfterScreenTimeout(5000)
            kosmos.fakeAuthenticationRepository.powerButtonInstantlyLocks = false
            val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)

            kosmos.fakeTrustRepository.setCurrentUserTrusted(true)

            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
                SuccessFingerprintAuthenticationStatus(0, true)
            )
            runCurrent()
            assertThat(deviceUnlockStatus?.isUnlocked).isTrue()

            kosmos.powerInteractor.setAsleepForTest(
                sleepReason = PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON
            )
            runCurrent()

            assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
        }

    @Test
    fun deviceUnlockStatus_staysUnlocked_whileIsTrusted() =
        testScope.runTest {
            val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)
            kosmos.fakeTrustRepository.setCurrentUserTrusted(true)
            unlockDevice()

            kosmos.powerInteractor.setAsleepForTest(
                sleepReason = PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON
            )
            runCurrent()

            assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
        }

    @Test
    fun deviceUnlockStatus_becomesLocked_whenNoLongerTrusted_whileAsleep() =
        testScope.runTest {
            val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)
            kosmos.fakeTrustRepository.setCurrentUserTrusted(true)
            unlockDevice()
            kosmos.powerInteractor.setAsleepForTest(
                sleepReason = PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON
            )
            runCurrent()
            assertThat(deviceUnlockStatus?.isUnlocked).isTrue()

            kosmos.fakeTrustRepository.setCurrentUserTrusted(false)
            runCurrent()

            assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
        }

    private fun TestScope.unlockDevice() {
        val deviceUnlockStatus by collectLastValue(underTest.deviceUnlockStatus)

+28 −0
Original line number Diff line number Diff line
@@ -2695,6 +2695,34 @@ class SceneContainerStartableTest : SysuiTestCase() {
            assertThat(isVisible).isFalse()
        }

    @Test
    fun deviceLocks_whenNoLongerTrusted_whileDeviceNotEntered() =
        testScope.runTest {
            prepareState(isDeviceUnlocked = true, initialSceneKey = Scenes.Gone)
            underTest.start()

            val isDeviceEntered by collectLastValue(kosmos.deviceEntryInteractor.isDeviceEntered)
            val deviceUnlockStatus by
                collectLastValue(kosmos.deviceUnlockedInteractor.deviceUnlockStatus)
            val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
            assertThat(isDeviceEntered).isTrue()
            assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
            assertThat(currentScene).isEqualTo(Scenes.Gone)
            kosmos.fakeTrustRepository.setCurrentUserTrusted(true)
            kosmos.sceneInteractor.changeScene(Scenes.Lockscreen, "reason")
            runCurrent()
            assertThat(isDeviceEntered).isFalse()
            assertThat(deviceUnlockStatus?.isUnlocked).isTrue()
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)

            kosmos.fakeTrustRepository.setCurrentUserTrusted(false)
            runCurrent()

            assertThat(isDeviceEntered).isFalse()
            assertThat(deviceUnlockStatus?.isUnlocked).isFalse()
            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
        }

    private fun TestScope.emulateSceneTransition(
        transitionStateFlow: MutableStateFlow<ObservableTransitionState>,
        toScene: SceneKey,
+2 −30
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.systemui.deviceentry.domain.interactor

import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.policy.IKeyguardDismissCallback
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
@@ -36,11 +35,9 @@ import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.utils.coroutines.flow.mapLatestConflated
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
@@ -285,32 +282,7 @@ constructor(
    }

    /** Locks the device instantly. */
    fun lockNow() {
        deviceUnlockedInteractor.lockNow()
    }

    suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
        coroutineScope {
            launch {
                isDeviceEntered
                    .logDiffsForTable(
                        tableLogBuffer = tableLogBuffer,
                        columnName = "isDeviceEntered",
                        initialValue = isDeviceEntered.value,
                    )
                    .collect()
            }

            launch {
                canSwipeToEnter
                    .map { it?.toString() ?: "" }
                    .logDiffsForTable(
                        tableLogBuffer = tableLogBuffer,
                        columnName = "canSwipeToEnter",
                        initialValue = canSwipeToEnter.value?.toString() ?: "",
                    )
                    .collect()
            }
        }
    fun lockNow(debuggingReason: String) {
        deviceUnlockedInteractor.lockNow(debuggingReason)
    }
}
+65 −40
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.receiveAsFlow
@@ -70,7 +71,7 @@ class DeviceUnlockedInteractor
constructor(
    private val authenticationInteractor: AuthenticationInteractor,
    private val repository: DeviceEntryRepository,
    trustInteractor: TrustInteractor,
    private val trustInteractor: TrustInteractor,
    faceAuthInteractor: DeviceEntryFaceAuthInteractor,
    fingerprintAuthInteractor: DeviceEntryFingerprintAuthInteractor,
    private val powerInteractor: PowerInteractor,
@@ -181,7 +182,8 @@ constructor(
    val deviceUnlockStatus: StateFlow<DeviceUnlockStatus> =
        repository.deviceUnlockStatus.asStateFlow()

    private val lockNowRequests = Channel<Unit>()
    /** A [Channel] of "lock now" requests where the values are the debugging reasons. */
    private val lockNowRequests = Channel<String>()

    override suspend fun onActivated(): Nothing {
        coroutineScope {
@@ -218,8 +220,8 @@ constructor(
    }

    /** Locks the device instantly. */
    fun lockNow() {
        lockNowRequests.trySend(Unit)
    fun lockNow(debuggingReason: String) {
        lockNowRequests.trySend(debuggingReason)
    }

    private suspend fun handleLockAndUnlockEvents() {
@@ -243,6 +245,21 @@ constructor(
    }

    private suspend fun handleLockEvents() {
        merge(
                trustInteractor.isTrusted.flatMapLatestConflated { isTrusted ->
                    if (isTrusted) {
                        // When entering a trusted environment, power-related lock events are
                        // ignored.
                        Log.d(TAG, "In trusted environment, ignoring power-related lock events")
                        flowOf(CancelDelayedLock("in trusted environment"))
                    } else {
                        // When not in a trusted environment, power-related lock events are treated
                        // as normal.
                        Log.d(
                            TAG,
                            "Not in trusted environment, power-related lock events treated as" +
                                " normal",
                        )
                        merge(
                            // Device wakefulness events.
                            powerInteractor.detailedWakefulness
@@ -252,10 +269,13 @@ constructor(
                                    if (isAsleep) {
                                        if (
                                            (lastSleepReason == WakeSleepReason.POWER_BUTTON) &&
                                    authenticationInteractor.getPowerButtonInstantlyLocks()
                                                authenticationInteractor
                                                    .getPowerButtonInstantlyLocks()
                                        ) {
                                            LockImmediately("locked instantly from power button")
                            } else if (lastSleepReason == WakeSleepReason.SLEEP_BUTTON) {
                                        } else if (
                                            lastSleepReason == WakeSleepReason.SLEEP_BUTTON
                                        ) {
                                            LockImmediately("locked instantly from sleep button")
                                        } else {
                                            LockWithDelay("entering sleep")
@@ -264,16 +284,13 @@ constructor(
                                        CancelDelayedLock("waking up")
                                    }
                                },
                // Device enters lockdown.
                isInLockdown
                    .distinctUntilChanged()
                    .filter { it }
                    .map { LockImmediately("lockdown") },
                            // Started dreaming
                            powerInteractor.isInteractive.flatMapLatestConflated { isInteractive ->
                    // Only respond to dream state changes while the device is interactive.
                                // Only respond to dream state changes while the device is
                                // interactive.
                                if (isInteractive) {
                        keyguardInteractor.isDreamingAny.distinctUntilChanged().map { isDreaming ->
                                    keyguardInteractor.isDreamingAny.distinctUntilChanged().map {
                                        isDreaming ->
                                        if (isDreaming) {
                                            LockWithDelay("started dreaming")
                                        } else {
@@ -284,7 +301,15 @@ constructor(
                                    emptyFlow()
                                }
                            },
                lockNowRequests.receiveAsFlow().map { LockImmediately("lockNow") },
                        )
                    }
                },
                // Device enters lockdown.
                isInLockdown
                    .distinctUntilChanged()
                    .filter { it }
                    .map { LockImmediately("lockdown") },
                lockNowRequests.receiveAsFlow().map { reason -> LockImmediately(reason) },
            )
            .collectLatest(::onLockEvent)
    }
+1 −1
Original line number Diff line number Diff line
@@ -671,7 +671,7 @@ public class KeyguardService extends Service {
            checkPermission();

            if (SceneContainerFlag.isEnabled()) {
                mDeviceEntryInteractorLazy.get().lockNow();
                mDeviceEntryInteractorLazy.get().lockNow("doKeyguardTimeout");
            } else if (KeyguardWmStateRefactor.isEnabled()) {
                mKeyguardServiceLockNowInteractor.onKeyguardServiceDoKeyguardTimeout(options);
            }
Loading