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

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

Merge "[flexiglass] Adds falsing protection when switching scenes." into main

parents 7c6ec7b7 9946c9d6
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -78,6 +78,7 @@ fun SceneContainer(
    val state: MutableSceneTransitionLayoutState = remember {
        MutableSceneTransitionLayoutState(
            initialScene = currentSceneKey.asComposeAware(),
            canChangeScene = { toScene -> viewModel.canChangeScene(toScene.asComposeUnaware()) },
            transitions = SceneContainerTransitions,
        )
    }
+101 −0
Original line number Diff line number Diff line
@@ -22,16 +22,20 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.classifier.fakeFalsingManager
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.sceneKeys
import com.android.systemui.scene.shared.flag.fakeSceneContainerFlags
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.fakeSceneDataSource
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -45,6 +49,8 @@ class SceneContainerViewModelTest : SysuiTestCase() {
    private val testScope by lazy { kosmos.testScope }
    private val interactor by lazy { kosmos.sceneInteractor }
    private val fakeSceneDataSource = kosmos.fakeSceneDataSource
    private val sceneContainerConfig = kosmos.sceneContainerConfig
    private val falsingManager = kosmos.fakeFalsingManager

    private lateinit var underTest: SceneContainerViewModel

@@ -86,4 +92,99 @@ class SceneContainerViewModelTest : SysuiTestCase() {

            assertThat(currentScene).isEqualTo(SceneKey.Shade)
        }

    @Test
    fun canChangeScene_whenAllowed_switchingFromGone_returnsTrue() =
        testScope.runTest {
            val currentScene by collectLastValue(underTest.currentScene)
            fakeSceneDataSource.changeScene(toScene = SceneKey.Gone)
            runCurrent()
            assertThat(currentScene).isEqualTo(SceneKey.Gone)

            sceneContainerConfig.sceneKeys
                .filter { it != currentScene }
                .forEach { toScene ->
                    assertWithMessage("Scene $toScene incorrectly protected when allowed")
                        .that(underTest.canChangeScene(toScene = toScene))
                        .isTrue()
                }
        }

    @Test
    fun canChangeScene_whenAllowed_switchingFromLockscreen_returnsTrue() =
        testScope.runTest {
            val currentScene by collectLastValue(underTest.currentScene)
            fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
            runCurrent()
            assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)

            sceneContainerConfig.sceneKeys
                .filter { it != currentScene }
                .forEach { toScene ->
                    assertWithMessage("Scene $toScene incorrectly protected when allowed")
                        .that(underTest.canChangeScene(toScene = toScene))
                        .isTrue()
                }
        }

    @Test
    fun canChangeScene_whenNotAllowed_fromLockscreen_toFalsingProtectedScenes_returnsFalse() =
        testScope.runTest {
            falsingManager.setIsFalseTouch(true)
            val currentScene by collectLastValue(underTest.currentScene)
            fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
            runCurrent()
            assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)

            sceneContainerConfig.sceneKeys
                .filter { it != currentScene }
                .filter {
                    // Moving to the Communal scene is not currently falsing protected.
                    it != SceneKey.Communal
                }
                .forEach { toScene ->
                    assertWithMessage("Protected scene $toScene not properly protected")
                        .that(underTest.canChangeScene(toScene = toScene))
                        .isFalse()
                }
        }

    @Test
    fun canChangeScene_whenNotAllowed_fromLockscreen_toFalsingUnprotectedScenes_returnsTrue() =
        testScope.runTest {
            falsingManager.setIsFalseTouch(true)
            val currentScene by collectLastValue(underTest.currentScene)
            fakeSceneDataSource.changeScene(toScene = SceneKey.Lockscreen)
            runCurrent()
            assertThat(currentScene).isEqualTo(SceneKey.Lockscreen)

            sceneContainerConfig.sceneKeys
                .filter {
                    // Moving to the Communal scene is not currently falsing protected.
                    it == SceneKey.Communal
                }
                .forEach { toScene ->
                    assertWithMessage("Unprotected scene $toScene is incorrectly protected")
                        .that(underTest.canChangeScene(toScene = toScene))
                        .isTrue()
                }
        }

    @Test
    fun canChangeScene_whenNotAllowed_fromGone_toAnyOtherScene_returnsTrue() =
        testScope.runTest {
            falsingManager.setIsFalseTouch(true)
            val currentScene by collectLastValue(underTest.currentScene)
            fakeSceneDataSource.changeScene(toScene = SceneKey.Gone)
            runCurrent()
            assertThat(currentScene).isEqualTo(SceneKey.Gone)

            sceneContainerConfig.sceneKeys
                .filter { it != currentScene }
                .forEach { toScene ->
                    assertWithMessage("Protected scene $toScene not properly protected")
                        .that(underTest.canChangeScene(toScene = toScene))
                        .isTrue()
                }
        }
}
+12 −3
Original line number Diff line number Diff line
@@ -17,23 +17,27 @@
package com.android.systemui.classifier.domain.interactor

import android.view.MotionEvent
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.FalsingClassifier
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.FalsingManager
import javax.inject.Inject

/**
 * Exposes the subset of the [FalsingCollector] API that's required by external callers.
 * Exposes the subset of the [FalsingCollector] and [FalsingManager] APIs that's required by
 * external callers.
 *
 * E.g. methods of [FalsingCollector] that are not exposed by this class don't need to be invoked by
 * external callers as they're already called by the scene framework.
 * E.g. methods of the above APIs that are not exposed by this class either don't need to be invoked
 * by external callers (as they're already called by the scene framework) or haven't been added yet.
 */
@SysUISingleton
class FalsingInteractor
@Inject
constructor(
    @FalsingCollectorActual private val collector: FalsingCollector,
    private val manager: FalsingManager,
) {
    /**
     * Notifies of a [MotionEvent] that passed through the UI.
@@ -62,4 +66,9 @@ constructor(
    fun updateFalseConfidence(
        result: FalsingClassifier.Result,
    ) = collector.updateFalseConfidence(result)

    /** Returns `true` if the gesture should be rejected. */
    fun isFalseTouch(
        @Classifier.InteractionType interactionType: Int,
    ): Boolean = manager.isFalseTouch(interactionType)
}
+29 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.scene.ui.viewmodel

import android.view.MotionEvent
import com.android.systemui.classifier.Classifier
import com.android.systemui.classifier.domain.interactor.FalsingInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -77,7 +78,33 @@ constructor(
        falsingInteractor.onMotionEventComplete()
    }

    companion object {
        private const val SCENE_TRANSITION_LOGGING_REASON = "user input"
    /**
     * Returns `true` if a change to [toScene] is currently allowed; `false` otherwise.
     *
     * This is invoked only for user-initiated transitions. The goal is to check with the falsing
     * system whether the change from the current scene to the given scene should be rejected due to
     * it being a false touch.
     */
    fun canChangeScene(toScene: SceneKey): Boolean {
        val interactionTypeOrNull =
            when (toScene) {
                SceneKey.Bouncer -> Classifier.BOUNCER_UNLOCK
                SceneKey.Gone -> Classifier.UNLOCK
                SceneKey.Shade -> Classifier.NOTIFICATION_DRAG_DOWN
                SceneKey.QuickSettings -> Classifier.QUICK_SETTINGS
                else -> null
            }

        return interactionTypeOrNull?.let { interactionType ->
            // It's important that the falsing system is always queried, even if no enforcement will
            // occur. This helps build up the right signal in the system.
            val isFalseTouch = falsingInteractor.isFalseTouch(interactionType)

            // Only enforce falsing if moving from the lockscreen scene to a new scene.
            val fromLockscreenScene = currentScene.value == SceneKey.Lockscreen

            !fromLockscreenScene || !isFalseTouch
        }
            ?: true
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -17,11 +17,13 @@
package com.android.systemui.classifier.domain.interactor

import com.android.systemui.classifier.falsingCollector
import com.android.systemui.classifier.falsingManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture

val Kosmos.falsingInteractor by Fixture {
    FalsingInteractor(
        collector = falsingCollector,
        manager = falsingManager,
    )
}