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

Commit 3cecfb16 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

[flexiglass] Prevent unintentional switching to the Gone scene.

For security purposes, require a separate method in SceneInteractor be
used if wanting to switch to the Gone scene.

Fix: 319298118
Test: manually verified that flexiglass still works and I can unlock,
relock, and unlock the device again
Test: new unit test methods added, existing ones all pass
Flag: ACONFIG com.android.systemui.scene_container DEVELOPMENT

Change-Id: If52184b3a9d26d77c4f2af7e27d5169240601904
parent e0da2be8
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants
import com.android.systemui.classifier.FalsingA11yDelegate
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -819,6 +820,8 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {

            // While listening, going from the bouncer scene to the gone scene, does dismiss the
            // keyguard.
            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
            runCurrent()
            sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason")
            sceneTransitionStateFlow.value =
                ObservableTransitionState.Transition(
+2 −0
Original line number Diff line number Diff line
@@ -336,6 +336,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.None
            )
            runCurrent()

            underTest.attemptDeviceEntry()

@@ -353,6 +354,7 @@ class DeviceEntryInteractorTest : SysuiTestCase() {
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.None
            )
            runCurrent()

            underTest.attemptDeviceEntry()

+115 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

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

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.domain.interactor.authenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class DeviceUnlockedInteractorTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val authenticationRepository = kosmos.fakeAuthenticationRepository
    private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository

    val underTest =
        DeviceUnlockedInteractor(
            applicationScope = testScope.backgroundScope,
            authenticationInteractor = kosmos.authenticationInteractor,
            deviceEntryRepository = deviceEntryRepository,
        )

    @Test
    fun isDeviceUnlocked_whenUnlockedAndAuthMethodIsNone_isTrue() =
        testScope.runTest {
            val isUnlocked by collectLastValue(underTest.isDeviceUnlocked)

            deviceEntryRepository.setUnlocked(true)
            authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)

            assertThat(isUnlocked).isTrue()
        }

    @Test
    fun isDeviceUnlocked_whenUnlockedAndAuthMethodIsPin_isTrue() =
        testScope.runTest {
            val isUnlocked by collectLastValue(underTest.isDeviceUnlocked)

            deviceEntryRepository.setUnlocked(true)
            authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)

            assertThat(isUnlocked).isTrue()
        }

    @Test
    fun isDeviceUnlocked_whenUnlockedAndAuthMethodIsSim_isFalse() =
        testScope.runTest {
            val isUnlocked by collectLastValue(underTest.isDeviceUnlocked)

            deviceEntryRepository.setUnlocked(true)
            authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Sim)

            assertThat(isUnlocked).isFalse()
        }

    @Test
    fun isDeviceUnlocked_whenLockedAndAuthMethodIsNone_isTrue() =
        testScope.runTest {
            val isUnlocked by collectLastValue(underTest.isDeviceUnlocked)

            deviceEntryRepository.setUnlocked(false)
            authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)

            assertThat(isUnlocked).isTrue()
        }

    @Test
    fun isDeviceUnlocked_whenLockedAndAuthMethodIsPin_isFalse() =
        testScope.runTest {
            val isUnlocked by collectLastValue(underTest.isDeviceUnlocked)

            deviceEntryRepository.setUnlocked(false)
            authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)

            assertThat(isUnlocked).isFalse()
        }

    @Test
    fun isDeviceUnlocked_whenLockedAndAuthMethodIsSim_isFalse() =
        testScope.runTest {
            val isUnlocked by collectLastValue(underTest.isDeviceUnlocked)

            deviceEntryRepository.setUnlocked(false)
            authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Sim)

            assertThat(isUnlocked).isFalse()
        }
}
+23 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.kosmos.testScope
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.scene.data.repository.sceneContainerRepository
@@ -33,6 +34,7 @@ import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -68,6 +70,27 @@ class SceneInteractorTest : SysuiTestCase() {
            assertThat(desiredScene).isEqualTo(SceneModel(SceneKey.Shade))
        }

    @Test
    fun changeScene_toGoneWhenUnl_doesNotThrow() =
        testScope.runTest {
            val desiredScene by collectLastValue(underTest.desiredScene)
            assertThat(desiredScene).isEqualTo(SceneModel(SceneKey.Lockscreen))

            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
            runCurrent()

            underTest.changeScene(SceneModel(SceneKey.Gone), "reason")
            assertThat(desiredScene).isEqualTo(SceneModel(SceneKey.Gone))
        }

    @Test(expected = IllegalStateException::class)
    fun changeScene_toGoneWhenStillLocked_throws() =
        testScope.runTest {
            kosmos.fakeDeviceEntryRepository.setUnlocked(false)

            underTest.changeScene(SceneModel(SceneKey.Gone), "reason")
        }

    @Test
    fun onSceneChanged() =
        testScope.runTest {
+14 −0
Original line number Diff line number Diff line
@@ -311,6 +311,10 @@ class SceneContainerStartableTest : SysuiTestCase() {
                    SceneKey.QuickSettings,
                )
                .forEachIndexed { index, sceneKey ->
                    if (sceneKey == SceneKey.Gone) {
                        kosmos.fakeDeviceEntryRepository.setUnlocked(true)
                        runCurrent()
                    }
                    sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
                    runCurrent()
                    verify(sysUiState, times(index)).commitUpdate(Display.DEFAULT_DISPLAY)
@@ -420,6 +424,8 @@ class SceneContainerStartableTest : SysuiTestCase() {
                }

            // Changing to the Gone scene should report a successful unlock.
            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
            runCurrent()
            sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
            runCurrent()
            verify(falsingCollector).onSuccessfulUnlock()
@@ -613,6 +619,8 @@ class SceneContainerStartableTest : SysuiTestCase() {
            runCurrent()
            verify(falsingCollector).onBouncerShown()

            kosmos.fakeDeviceEntryRepository.setUnlocked(true)
            runCurrent()
            sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
            runCurrent()
            verify(falsingCollector, times(2)).onBouncerHidden()
@@ -741,9 +749,15 @@ class SceneContainerStartableTest : SysuiTestCase() {
                "Lockscreen cannot be disabled while having a secure authentication method"
            }
        }

        check(initialSceneKey != SceneKey.Gone || isDeviceUnlocked) {
            "Cannot start on the Gone scene and have the device be locked at the same time."
        }

        sceneContainerFlags.enabled = true
        kosmos.fakeDeviceEntryRepository.setUnlocked(isDeviceUnlocked)
        kosmos.fakeDeviceEntryRepository.setBypassEnabled(isBypassEnabled)
        runCurrent()
        val transitionStateFlow =
            MutableStateFlow<ObservableTransitionState>(
                ObservableTransitionState.Idle(SceneKey.Lockscreen)
Loading