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

Commit a0d7d457 authored by Aaron Liu's avatar Aaron Liu
Browse files

Ensure that flow is run on main dispatcher

When adding and removing the callback for KeyguardUpdateMonitor, there
is an assertion that the operation must be on the main thread. The
callback was being removed on a worker thread. We can ensure that this
is not the case with flowOn.

Fixes: 274558013
Test: restart device and unlock the device several times.
Change-Id: I1f40e8b7f2cf7245f0cb17e2d0e10b9ece619234
parent 8b884802
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLoggin
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.doze.DozeHost
import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback
@@ -43,12 +44,14 @@ import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnl
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn

/** Defines interface for classes that encapsulate application state for the keyguard. */
interface KeyguardRepository {
@@ -195,6 +198,7 @@ constructor(
    private val dozeParameters: DozeParameters,
    private val authController: AuthController,
    private val dreamOverlayCallbackController: DreamOverlayCallbackController,
    @Main private val mainDispatcher: CoroutineDispatcher
) : KeyguardRepository {
    private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
    override val animateBottomAreaDozingTransitions =
@@ -387,6 +391,7 @@ constructor(

                awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
            }
            .flowOn(mainDispatcher)
            .distinctUntilChanged()

    override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {
+79 −21
Original line number Diff line number Diff line
@@ -46,10 +46,12 @@ import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -59,6 +61,7 @@ import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardRepositoryImplTest : SysuiTestCase() {
@@ -73,6 +76,9 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
    @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
    @Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController
    @Mock private lateinit var dozeParameters: DozeParameters
    private val mainDispatcher = StandardTestDispatcher()
    private val testDispatcher = StandardTestDispatcher()
    private val testScope = TestScope(testDispatcher)

    private lateinit var underTest: KeyguardRepositoryImpl

@@ -92,12 +98,13 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                dozeParameters,
                authController,
                dreamOverlayCallbackController,
                mainDispatcher
            )
    }

    @Test
    fun animateBottomAreaDozingTransitions() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            assertThat(underTest.animateBottomAreaDozingTransitions.value).isEqualTo(false)

            underTest.setAnimateDozingTransitions(true)
@@ -112,7 +119,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun bottomAreaAlpha() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)

            underTest.setBottomAreaAlpha(0.1f)
@@ -133,7 +140,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun clockPosition() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 0))

            underTest.setClockPosition(0, 1)
@@ -151,11 +158,12 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun isKeyguardShowing() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            whenever(keyguardStateController.isShowing).thenReturn(false)
            var latest: Boolean? = null
            val job = underTest.isKeyguardShowing.onEach { latest = it }.launchIn(this)

            runCurrent()
            assertThat(latest).isFalse()
            assertThat(underTest.isKeyguardShowing()).isFalse()

@@ -164,11 +172,13 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

            whenever(keyguardStateController.isShowing).thenReturn(true)
            captor.value.onKeyguardShowingChanged()
            runCurrent()
            assertThat(latest).isTrue()
            assertThat(underTest.isKeyguardShowing()).isTrue()

            whenever(keyguardStateController.isShowing).thenReturn(false)
            captor.value.onKeyguardShowingChanged()
            runCurrent()
            assertThat(latest).isFalse()
            assertThat(underTest.isKeyguardShowing()).isFalse()

@@ -197,11 +207,12 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun isKeyguardOccluded() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            whenever(keyguardStateController.isOccluded).thenReturn(false)
            var latest: Boolean? = null
            val job = underTest.isKeyguardOccluded.onEach { latest = it }.launchIn(this)

            runCurrent()
            assertThat(latest).isFalse()

            val captor = argumentCaptor<KeyguardStateController.Callback>()
@@ -209,10 +220,12 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

            whenever(keyguardStateController.isOccluded).thenReturn(true)
            captor.value.onKeyguardShowingChanged()
            runCurrent()
            assertThat(latest).isTrue()

            whenever(keyguardStateController.isOccluded).thenReturn(false)
            captor.value.onKeyguardShowingChanged()
            runCurrent()
            assertThat(latest).isFalse()

            job.cancel()
@@ -220,11 +233,12 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun isKeyguardUnlocked() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            whenever(keyguardStateController.isUnlocked).thenReturn(false)
            var latest: Boolean? = null
            val job = underTest.isKeyguardUnlocked.onEach { latest = it }.launchIn(this)

            runCurrent()
            assertThat(latest).isFalse()

            val captor = argumentCaptor<KeyguardStateController.Callback>()
@@ -232,10 +246,12 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

            whenever(keyguardStateController.isUnlocked).thenReturn(true)
            captor.value.onUnlockedChanged()
            runCurrent()
            assertThat(latest).isTrue()

            whenever(keyguardStateController.isUnlocked).thenReturn(false)
            captor.value.onUnlockedChanged()
            runCurrent()
            assertThat(latest).isFalse()

            job.cancel()
@@ -243,82 +259,98 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun isDozing() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            var latest: Boolean? = null
            val job = underTest.isDozing.onEach { latest = it }.launchIn(this)

            runCurrent()
            val captor = argumentCaptor<DozeHost.Callback>()
            verify(dozeHost).addCallback(captor.capture())

            captor.value.onDozingChanged(true)
            runCurrent()
            assertThat(latest).isTrue()

            captor.value.onDozingChanged(false)
            runCurrent()
            assertThat(latest).isFalse()

            job.cancel()
            runCurrent()
            verify(dozeHost).removeCallback(captor.value)
        }

    @Test
    fun `isDozing - starts with correct initial value for isDozing`() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            var latest: Boolean? = null

            whenever(statusBarStateController.isDozing).thenReturn(true)
            var job = underTest.isDozing.onEach { latest = it }.launchIn(this)
            runCurrent()
            assertThat(latest).isTrue()
            job.cancel()

            whenever(statusBarStateController.isDozing).thenReturn(false)
            job = underTest.isDozing.onEach { latest = it }.launchIn(this)
            runCurrent()
            assertThat(latest).isFalse()
            job.cancel()
        }

    @Test
    fun dozeAmount() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            val values = mutableListOf<Float>()
            val job = underTest.linearDozeAmount.onEach(values::add).launchIn(this)

            val captor = argumentCaptor<StatusBarStateController.StateListener>()
            runCurrent()
            verify(statusBarStateController).addCallback(captor.capture())

            captor.value.onDozeAmountChanged(0.433f, 0.4f)
            runCurrent()
            captor.value.onDozeAmountChanged(0.498f, 0.5f)
            runCurrent()
            captor.value.onDozeAmountChanged(0.661f, 0.65f)
            runCurrent()

            assertThat(values).isEqualTo(listOf(0f, 0.433f, 0.498f, 0.661f))

            job.cancel()
            runCurrent()
            verify(statusBarStateController).removeCallback(captor.value)
        }

    @Test
    fun wakefulness() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            val values = mutableListOf<WakefulnessModel>()
            val job = underTest.wakefulness.onEach(values::add).launchIn(this)

            runCurrent()
            val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
            verify(wakefulnessLifecycle).addObserver(captor.capture())

            whenever(wakefulnessLifecycle.wakefulness)
                .thenReturn(WakefulnessLifecycle.WAKEFULNESS_WAKING)
            captor.value.onStartedWakingUp()
            runCurrent()

            whenever(wakefulnessLifecycle.wakefulness)
                .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE)
            captor.value.onFinishedWakingUp()
            runCurrent()

            whenever(wakefulnessLifecycle.wakefulness)
                .thenReturn(WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP)
            captor.value.onStartedGoingToSleep()
            runCurrent()

            whenever(wakefulnessLifecycle.wakefulness)
                .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
            captor.value.onFinishedGoingToSleep()
            runCurrent()

            assertThat(values.map { it.state })
                .isEqualTo(
@@ -333,12 +365,13 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                )

            job.cancel()
            runCurrent()
            verify(wakefulnessLifecycle).removeObserver(captor.value)
        }

    @Test
    fun isUdfpsSupported() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
            assertThat(underTest.isUdfpsSupported()).isTrue()

@@ -348,11 +381,11 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun isKeyguardGoingAway() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
            var latest: Boolean? = null
            val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this)

            runCurrent()
            assertThat(latest).isFalse()

            val captor = argumentCaptor<KeyguardStateController.Callback>()
@@ -360,10 +393,12 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

            whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
            captor.value.onKeyguardGoingAwayChanged()
            runCurrent()
            assertThat(latest).isTrue()

            whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
            captor.value.onKeyguardGoingAwayChanged()
            runCurrent()
            assertThat(latest).isFalse()

            job.cancel()
@@ -371,20 +406,23 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun isDreamingFromKeyguardUpdateMonitor() =
        runTest(UnconfinedTestDispatcher()) {
        TestScope(mainDispatcher).runTest {
            whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
            var latest: Boolean? = null
            val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)

            runCurrent()
            assertThat(latest).isFalse()

            val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
            verify(keyguardUpdateMonitor).registerCallback(captor.capture())

            captor.value.onDreamingStateChanged(true)
            runCurrent()
            assertThat(latest).isTrue()

            captor.value.onDreamingStateChanged(false)
            runCurrent()
            assertThat(latest).isFalse()

            job.cancel()
@@ -392,11 +430,12 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun isDreamingFromDreamOverlayCallbackController() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            whenever(dreamOverlayCallbackController.isDreaming).thenReturn(false)
            var latest: Boolean? = null
            val job = underTest.isDreamingWithOverlay.onEach { latest = it }.launchIn(this)

            runCurrent()
            assertThat(latest).isFalse()

            val listener =
@@ -405,9 +444,11 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                }

            listener.onStartDream()
            runCurrent()
            assertThat(latest).isTrue()

            listener.onWakeUp()
            runCurrent()
            assertThat(latest).isFalse()

            job.cancel()
@@ -415,10 +456,11 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun biometricUnlockState() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            val values = mutableListOf<BiometricUnlockModel>()
            val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)

            runCurrent()
            val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
            verify(biometricUnlockController).addBiometricModeListener(captor.capture())

@@ -435,6 +477,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                .forEach {
                    whenever(biometricUnlockController.mode).thenReturn(it)
                    captor.value.onModeChanged(it)
                    runCurrent()
                }

            assertThat(values)
@@ -454,12 +497,13 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                )

            job.cancel()
            runCurrent()
            verify(biometricUnlockController).removeBiometricModeListener(captor.value)
        }

    @Test
    fun dozeTransitionModel() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            // For the initial state
            whenever(dozeTransitionListener.oldState).thenReturn(DozeMachine.State.UNINITIALIZED)
            whenever(dozeTransitionListener.newState).thenReturn(DozeMachine.State.UNINITIALIZED)
@@ -467,6 +511,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
            val values = mutableListOf<DozeTransitionModel>()
            val job = underTest.dozeTransitionModel.onEach(values::add).launchIn(this)

            runCurrent()
            val listener =
                withArgCaptor<DozeTransitionCallback> {
                    verify(dozeTransitionListener).addCallback(capture())
@@ -475,20 +520,26 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
            // These don't have to reflect real transitions from the DozeMachine. Only that the
            // transitions are properly emitted
            listener.onDozeTransition(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE)
            runCurrent()
            listener.onDozeTransition(DozeMachine.State.DOZE, DozeMachine.State.DOZE_AOD)
            runCurrent()
            listener.onDozeTransition(DozeMachine.State.DOZE_AOD_DOCKED, DozeMachine.State.FINISH)
            runCurrent()
            listener.onDozeTransition(
                DozeMachine.State.DOZE_REQUEST_PULSE,
                DozeMachine.State.DOZE_PULSING
            )
            runCurrent()
            listener.onDozeTransition(
                DozeMachine.State.DOZE_SUSPEND_TRIGGERS,
                DozeMachine.State.DOZE_PULSE_DONE
            )
            runCurrent()
            listener.onDozeTransition(
                DozeMachine.State.DOZE_AOD_PAUSING,
                DozeMachine.State.DOZE_AOD_PAUSED
            )
            runCurrent()

            assertThat(values)
                .isEqualTo(
@@ -517,15 +568,17 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                )

            job.cancel()
            runCurrent()
            verify(dozeTransitionListener).removeCallback(listener)
        }

    @Test
    fun fingerprintSensorLocation() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            val values = mutableListOf<Point?>()
            val job = underTest.fingerprintSensorLocation.onEach(values::add).launchIn(this)

            runCurrent()
            val captor = argumentCaptor<AuthController.Callback>()
            verify(authController).addCallback(captor.capture())

@@ -539,6 +592,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                .onEach {
                    whenever(authController.fingerprintSensorLocation).thenReturn(it)
                    captor.value.onFingerprintLocationChanged()
                    runCurrent()
                }
                .also { dispatchedSensorLocations ->
                    assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
@@ -549,11 +603,12 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun faceSensorLocation() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            val values = mutableListOf<Point?>()
            val job = underTest.faceSensorLocation.onEach(values::add).launchIn(this)

            val captor = argumentCaptor<AuthController.Callback>()
            runCurrent()
            verify(authController).addCallback(captor.capture())

            // An initial, null value should be initially emitted so that flows combined with this
@@ -571,6 +626,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                .onEach {
                    whenever(authController.faceSensorLocation).thenReturn(it)
                    captor.value.onFaceSensorLocationChanged()
                    runCurrent()
                }
                .also { dispatchedSensorLocations ->
                    assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
@@ -581,10 +637,11 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {

    @Test
    fun biometricUnlockSource() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            val values = mutableListOf<BiometricUnlockSource?>()
            val job = underTest.biometricUnlockSource.onEach(values::add).launchIn(this)

            runCurrent()
            val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
            verify(keyguardUpdateMonitor).registerCallback(captor.capture())

@@ -603,6 +660,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
                )
                .onEach { biometricSourceType ->
                    captor.value.onBiometricAuthenticated(0, biometricSourceType, false)
                    runCurrent()
                }

            assertThat(values)