blob: 0e6f8d4e0720e45deaf793c50f36b2408e1ae7da [file] [log] [blame]
/*
* Copyright (C) 2022 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.keyguard.data.repository
import android.graphics.Point
import android.hardware.biometrics.BiometricSourceType
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.shared.model.Position
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.DozeHost
import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.dreams.DreamOverlayCallbackController
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.KeyguardStateController
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.flow.launchIn
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
@Mock private lateinit var authController: AuthController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController
@Mock private lateinit var dozeParameters: DozeParameters
private lateinit var underTest: KeyguardRepositoryImpl
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
underTest =
KeyguardRepositoryImpl(
statusBarStateController,
dozeHost,
wakefulnessLifecycle,
biometricUnlockController,
keyguardStateController,
keyguardUpdateMonitor,
dozeTransitionListener,
dozeParameters,
authController,
dreamOverlayCallbackController,
)
}
@Test
fun animateBottomAreaDozingTransitions() =
runTest(UnconfinedTestDispatcher()) {
assertThat(underTest.animateBottomAreaDozingTransitions.value).isEqualTo(false)
underTest.setAnimateDozingTransitions(true)
assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
underTest.setAnimateDozingTransitions(false)
assertThat(underTest.animateBottomAreaDozingTransitions.value).isFalse()
underTest.setAnimateDozingTransitions(true)
assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
}
@Test
fun bottomAreaAlpha() =
runTest(UnconfinedTestDispatcher()) {
assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
underTest.setBottomAreaAlpha(0.1f)
assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.1f)
underTest.setBottomAreaAlpha(0.2f)
assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.2f)
underTest.setBottomAreaAlpha(0.3f)
assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.3f)
underTest.setBottomAreaAlpha(0.5f)
assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.5f)
underTest.setBottomAreaAlpha(1.0f)
assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
}
@Test
fun clockPosition() =
runTest(UnconfinedTestDispatcher()) {
assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 0))
underTest.setClockPosition(0, 1)
assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 1))
underTest.setClockPosition(1, 9)
assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 9))
underTest.setClockPosition(1, 0)
assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 0))
underTest.setClockPosition(3, 1)
assertThat(underTest.clockPosition.value).isEqualTo(Position(3, 1))
}
@Test
fun isKeyguardShowing() =
runTest(UnconfinedTestDispatcher()) {
whenever(keyguardStateController.isShowing).thenReturn(false)
var latest: Boolean? = null
val job = underTest.isKeyguardShowing.onEach { latest = it }.launchIn(this)
assertThat(latest).isFalse()
assertThat(underTest.isKeyguardShowing()).isFalse()
val captor = argumentCaptor<KeyguardStateController.Callback>()
verify(keyguardStateController).addCallback(captor.capture())
whenever(keyguardStateController.isShowing).thenReturn(true)
captor.value.onKeyguardShowingChanged()
assertThat(latest).isTrue()
assertThat(underTest.isKeyguardShowing()).isTrue()
whenever(keyguardStateController.isShowing).thenReturn(false)
captor.value.onKeyguardShowingChanged()
assertThat(latest).isFalse()
assertThat(underTest.isKeyguardShowing()).isFalse()
job.cancel()
}
@Test
fun isAodAvailable() = runTest {
val flow = underTest.isAodAvailable
var isAodAvailable = collectLastValue(flow)
runCurrent()
val callback =
withArgCaptor<DozeParameters.Callback> { verify(dozeParameters).addCallback(capture()) }
whenever(dozeParameters.getAlwaysOn()).thenReturn(false)
callback.onAlwaysOnChange()
assertThat(isAodAvailable()).isEqualTo(false)
whenever(dozeParameters.getAlwaysOn()).thenReturn(true)
callback.onAlwaysOnChange()
assertThat(isAodAvailable()).isEqualTo(true)
flow.onCompletion { verify(dozeParameters).removeCallback(callback) }
}
@Test
fun isKeyguardOccluded() =
runTest(UnconfinedTestDispatcher()) {
whenever(keyguardStateController.isOccluded).thenReturn(false)
var latest: Boolean? = null
val job = underTest.isKeyguardOccluded.onEach { latest = it }.launchIn(this)
assertThat(latest).isFalse()
val captor = argumentCaptor<KeyguardStateController.Callback>()
verify(keyguardStateController).addCallback(captor.capture())
whenever(keyguardStateController.isOccluded).thenReturn(true)
captor.value.onKeyguardShowingChanged()
assertThat(latest).isTrue()
whenever(keyguardStateController.isOccluded).thenReturn(false)
captor.value.onKeyguardShowingChanged()
assertThat(latest).isFalse()
job.cancel()
}
@Test
fun isKeyguardUnlocked() =
runTest(UnconfinedTestDispatcher()) {
whenever(keyguardStateController.isUnlocked).thenReturn(false)
var latest: Boolean? = null
val job = underTest.isKeyguardUnlocked.onEach { latest = it }.launchIn(this)
assertThat(latest).isFalse()
val captor = argumentCaptor<KeyguardStateController.Callback>()
verify(keyguardStateController).addCallback(captor.capture())
whenever(keyguardStateController.isUnlocked).thenReturn(true)
captor.value.onUnlockedChanged()
assertThat(latest).isTrue()
whenever(keyguardStateController.isUnlocked).thenReturn(false)
captor.value.onUnlockedChanged()
assertThat(latest).isFalse()
job.cancel()
}
@Test
fun isDozing() =
runTest(UnconfinedTestDispatcher()) {
var latest: Boolean? = null
val job = underTest.isDozing.onEach { latest = it }.launchIn(this)
val captor = argumentCaptor<DozeHost.Callback>()
verify(dozeHost).addCallback(captor.capture())
captor.value.onDozingChanged(true)
assertThat(latest).isTrue()
captor.value.onDozingChanged(false)
assertThat(latest).isFalse()
job.cancel()
verify(dozeHost).removeCallback(captor.value)
}
@Test
fun `isDozing - starts with correct initial value for isDozing`() =
runTest(UnconfinedTestDispatcher()) {
var latest: Boolean? = null
whenever(statusBarStateController.isDozing).thenReturn(true)
var job = underTest.isDozing.onEach { latest = it }.launchIn(this)
assertThat(latest).isTrue()
job.cancel()
whenever(statusBarStateController.isDozing).thenReturn(false)
job = underTest.isDozing.onEach { latest = it }.launchIn(this)
assertThat(latest).isFalse()
job.cancel()
}
@Test
fun dozeAmount() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
val job = underTest.linearDozeAmount.onEach(values::add).launchIn(this)
val captor = argumentCaptor<StatusBarStateController.StateListener>()
verify(statusBarStateController).addCallback(captor.capture())
captor.value.onDozeAmountChanged(0.433f, 0.4f)
captor.value.onDozeAmountChanged(0.498f, 0.5f)
captor.value.onDozeAmountChanged(0.661f, 0.65f)
assertThat(values).isEqualTo(listOf(0f, 0.433f, 0.498f, 0.661f))
job.cancel()
verify(statusBarStateController).removeCallback(captor.value)
}
@Test
fun wakefulness() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<WakefulnessModel>()
val job = underTest.wakefulness.onEach(values::add).launchIn(this)
val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
verify(wakefulnessLifecycle).addObserver(captor.capture())
whenever(wakefulnessLifecycle.wakefulness)
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_WAKING)
captor.value.onStartedWakingUp()
whenever(wakefulnessLifecycle.wakefulness)
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE)
captor.value.onFinishedWakingUp()
whenever(wakefulnessLifecycle.wakefulness)
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP)
captor.value.onStartedGoingToSleep()
whenever(wakefulnessLifecycle.wakefulness)
.thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
captor.value.onFinishedGoingToSleep()
assertThat(values.map { it.state })
.isEqualTo(
listOf(
// Initial value will be ASLEEP
WakefulnessState.ASLEEP,
WakefulnessState.STARTING_TO_WAKE,
WakefulnessState.AWAKE,
WakefulnessState.STARTING_TO_SLEEP,
WakefulnessState.ASLEEP,
)
)
job.cancel()
verify(wakefulnessLifecycle).removeObserver(captor.value)
}
@Test
fun isUdfpsSupported() =
runTest(UnconfinedTestDispatcher()) {
whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
assertThat(underTest.isUdfpsSupported()).isTrue()
whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(false)
assertThat(underTest.isUdfpsSupported()).isFalse()
}
@Test
fun isKeyguardGoingAway() =
runTest(UnconfinedTestDispatcher()) {
whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
var latest: Boolean? = null
val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this)
assertThat(latest).isFalse()
val captor = argumentCaptor<KeyguardStateController.Callback>()
verify(keyguardStateController).addCallback(captor.capture())
whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
captor.value.onKeyguardGoingAwayChanged()
assertThat(latest).isTrue()
whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
captor.value.onKeyguardGoingAwayChanged()
assertThat(latest).isFalse()
job.cancel()
}
@Test
fun isDreamingFromKeyguardUpdateMonitor() =
runTest(UnconfinedTestDispatcher()) {
whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
var latest: Boolean? = null
val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
assertThat(latest).isFalse()
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(captor.capture())
captor.value.onDreamingStateChanged(true)
assertThat(latest).isTrue()
captor.value.onDreamingStateChanged(false)
assertThat(latest).isFalse()
job.cancel()
}
@Test
fun isDreamingFromDreamOverlayCallbackController() =
runTest(UnconfinedTestDispatcher()) {
whenever(dreamOverlayCallbackController.isDreaming).thenReturn(false)
var latest: Boolean? = null
val job = underTest.isDreamingWithOverlay.onEach { latest = it }.launchIn(this)
assertThat(latest).isFalse()
val listener =
withArgCaptor<DreamOverlayCallbackController.Callback> {
verify(dreamOverlayCallbackController).addCallback(capture())
}
listener.onStartDream()
assertThat(latest).isTrue()
listener.onWakeUp()
assertThat(latest).isFalse()
job.cancel()
}
@Test
fun biometricUnlockState() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<BiometricUnlockModel>()
val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
verify(biometricUnlockController).addBiometricModeListener(captor.capture())
listOf(
BiometricUnlockController.MODE_NONE,
BiometricUnlockController.MODE_WAKE_AND_UNLOCK,
BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING,
BiometricUnlockController.MODE_SHOW_BOUNCER,
BiometricUnlockController.MODE_ONLY_WAKE,
BiometricUnlockController.MODE_UNLOCK_COLLAPSING,
BiometricUnlockController.MODE_DISMISS_BOUNCER,
BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM,
)
.forEach {
whenever(biometricUnlockController.mode).thenReturn(it)
captor.value.onModeChanged(it)
}
assertThat(values)
.isEqualTo(
listOf(
// Initial value will be NONE, followed by onModeChanged() call
BiometricUnlockModel.NONE,
BiometricUnlockModel.NONE,
BiometricUnlockModel.WAKE_AND_UNLOCK,
BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
BiometricUnlockModel.SHOW_BOUNCER,
BiometricUnlockModel.ONLY_WAKE,
BiometricUnlockModel.UNLOCK_COLLAPSING,
BiometricUnlockModel.DISMISS_BOUNCER,
BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM,
)
)
job.cancel()
verify(biometricUnlockController).removeBiometricModeListener(captor.value)
}
@Test
fun dozeTransitionModel() =
runTest(UnconfinedTestDispatcher()) {
// For the initial state
whenever(dozeTransitionListener.oldState).thenReturn(DozeMachine.State.UNINITIALIZED)
whenever(dozeTransitionListener.newState).thenReturn(DozeMachine.State.UNINITIALIZED)
val values = mutableListOf<DozeTransitionModel>()
val job = underTest.dozeTransitionModel.onEach(values::add).launchIn(this)
val listener =
withArgCaptor<DozeTransitionCallback> {
verify(dozeTransitionListener).addCallback(capture())
}
// 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)
listener.onDozeTransition(DozeMachine.State.DOZE, DozeMachine.State.DOZE_AOD)
listener.onDozeTransition(DozeMachine.State.DOZE_AOD_DOCKED, DozeMachine.State.FINISH)
listener.onDozeTransition(
DozeMachine.State.DOZE_REQUEST_PULSE,
DozeMachine.State.DOZE_PULSING
)
listener.onDozeTransition(
DozeMachine.State.DOZE_SUSPEND_TRIGGERS,
DozeMachine.State.DOZE_PULSE_DONE
)
listener.onDozeTransition(
DozeMachine.State.DOZE_AOD_PAUSING,
DozeMachine.State.DOZE_AOD_PAUSED
)
assertThat(values)
.isEqualTo(
listOf(
// Initial value will be UNINITIALIZED
DozeTransitionModel(
DozeStateModel.UNINITIALIZED,
DozeStateModel.UNINITIALIZED
),
DozeTransitionModel(DozeStateModel.INITIALIZED, DozeStateModel.DOZE),
DozeTransitionModel(DozeStateModel.DOZE, DozeStateModel.DOZE_AOD),
DozeTransitionModel(DozeStateModel.DOZE_AOD_DOCKED, DozeStateModel.FINISH),
DozeTransitionModel(
DozeStateModel.DOZE_REQUEST_PULSE,
DozeStateModel.DOZE_PULSING
),
DozeTransitionModel(
DozeStateModel.DOZE_SUSPEND_TRIGGERS,
DozeStateModel.DOZE_PULSE_DONE
),
DozeTransitionModel(
DozeStateModel.DOZE_AOD_PAUSING,
DozeStateModel.DOZE_AOD_PAUSED
),
)
)
job.cancel()
verify(dozeTransitionListener).removeCallback(listener)
}
@Test
fun fingerprintSensorLocation() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Point?>()
val job = underTest.fingerprintSensorLocation.onEach(values::add).launchIn(this)
val captor = argumentCaptor<AuthController.Callback>()
verify(authController).addCallback(captor.capture())
// An initial, null value should be initially emitted so that flows combined with this
// one
// emit values immediately. The sensor location is expected to be nullable, so anyone
// consuming it should handle that properly.
assertThat(values).isEqualTo(listOf(null))
listOf(Point(500, 500), Point(0, 0), null, Point(250, 250))
.onEach {
whenever(authController.fingerprintSensorLocation).thenReturn(it)
captor.value.onFingerprintLocationChanged()
}
.also { dispatchedSensorLocations ->
assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
}
job.cancel()
}
@Test
fun faceSensorLocation() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Point?>()
val job = underTest.faceSensorLocation.onEach(values::add).launchIn(this)
val captor = argumentCaptor<AuthController.Callback>()
verify(authController).addCallback(captor.capture())
// An initial, null value should be initially emitted so that flows combined with this
// one
// emit values immediately. The sensor location is expected to be nullable, so anyone
// consuming it should handle that properly.
assertThat(values).isEqualTo(listOf(null))
listOf(
Point(500, 500),
Point(0, 0),
null,
Point(250, 250),
)
.onEach {
whenever(authController.faceSensorLocation).thenReturn(it)
captor.value.onFaceSensorLocationChanged()
}
.also { dispatchedSensorLocations ->
assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
}
job.cancel()
}
@Test
fun biometricUnlockSource() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<BiometricUnlockSource?>()
val job = underTest.biometricUnlockSource.onEach(values::add).launchIn(this)
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(captor.capture())
// An initial, null value should be initially emitted so that flows combined with this
// one
// emit values immediately. The biometric unlock source is expected to be nullable, so
// anyone consuming it should handle that properly.
assertThat(values).isEqualTo(listOf(null))
listOf(
BiometricSourceType.FINGERPRINT,
BiometricSourceType.IRIS,
null,
BiometricSourceType.FACE,
BiometricSourceType.FINGERPRINT,
)
.onEach { biometricSourceType ->
captor.value.onBiometricAuthenticated(0, biometricSourceType, false)
}
assertThat(values)
.isEqualTo(
listOf(
null,
BiometricUnlockSource.FINGERPRINT_SENSOR,
BiometricUnlockSource.FACE_SENSOR,
null,
BiometricUnlockSource.FACE_SENSOR,
BiometricUnlockSource.FINGERPRINT_SENSOR,
)
)
job.cancel()
}
}