Loading packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/binder/LiftToRunFaceAuthBinderTest.kt 0 → 100644 +200 −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.ui.binder import android.content.packageManager import android.content.pm.PackageManager import android.hardware.Sensor import android.hardware.TriggerEventListener import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.deviceentry.ui.binder.liftToRunFaceAuthBinder import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.kosmos.testScope import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.whenever import com.android.systemui.util.sensors.asyncSensorManager import kotlinx.coroutines.ExperimentalCoroutinesApi 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.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @ExperimentalCoroutinesApi @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class LiftToRunFaceAuthBinderTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val sensorManager = kosmos.asyncSensorManager private val powerRepository = kosmos.fakePowerRepository private val keyguardRepository = kosmos.fakeKeyguardRepository private val bouncerRepository = kosmos.keyguardBouncerRepository private val biometricSettingsRepository = kosmos.biometricSettingsRepository private val packageManager = kosmos.packageManager @Captor private lateinit var triggerEventListenerCaptor: ArgumentCaptor<TriggerEventListener> @Mock private lateinit var mockSensor: Sensor private val underTest = kosmos.liftToRunFaceAuthBinder @Before fun setup() { MockitoAnnotations.initMocks(this) whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true) whenever(sensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)).thenReturn(mockSensor) } @Test fun doNotListenForGesture() = testScope.runTest { start() verifyNeverRequestsTriggerSensor() } @Test fun awakeKeyguard_listenForGesture() = testScope.runTest { start() givenAwakeKeyguard(true) runCurrent() verifyRequestTriggerSensor() } @Test fun faceNotEnrolled_listenForGesture() = testScope.runTest { start() givenAwakeKeyguard(true) biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) runCurrent() verifyNeverRequestsTriggerSensor() } @Test fun notInteractive_doNotListenForGesture() = testScope.runTest { start() givenAwakeKeyguard(true) powerRepository.setInteractive(false) runCurrent() verifyNeverRequestsTriggerSensor() } @Test fun primaryBouncer_listenForGesture() = testScope.runTest { start() givenAwakeKeyguard(false) givenPrimaryBouncerShowing() runCurrent() verifyRequestTriggerSensor() } @Test fun alternateBouncer_listenForGesture() = testScope.runTest { start() givenAwakeKeyguard(false) givenAlternateBouncerShowing() runCurrent() verifyRequestTriggerSensor() } @Test fun restartListeningForGestureAfterSensorTrigger() = testScope.runTest { start() givenAwakeKeyguard(true) runCurrent() verifyRequestTriggerSensor() clearInvocations(sensorManager) triggerEventListenerCaptor.value.onTrigger(null) runCurrent() verifyRequestTriggerSensor() } @Test fun cancelTriggerSensor_keyguardNotAwakeAnymore() = testScope.runTest { start() givenAwakeKeyguard(true) runCurrent() verifyRequestTriggerSensor() givenAwakeKeyguard(false) runCurrent() verifyCancelTriggerSensor() } private fun start() { underTest.start() biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) givenAwakeKeyguard(false) givenBouncerNotShowing() } private fun givenAwakeKeyguard(isAwake: Boolean) { powerRepository.setInteractive(isAwake) keyguardRepository.setKeyguardShowing(isAwake) keyguardRepository.setKeyguardOccluded(false) } private fun givenPrimaryBouncerShowing() { bouncerRepository.setPrimaryShow(true) bouncerRepository.setAlternateVisible(false) } private fun givenBouncerNotShowing() { bouncerRepository.setPrimaryShow(false) bouncerRepository.setAlternateVisible(false) } private fun givenAlternateBouncerShowing() { bouncerRepository.setPrimaryShow(false) bouncerRepository.setAlternateVisible(true) } private fun verifyRequestTriggerSensor() { verify(sensorManager).requestTriggerSensor(capture(triggerEventListenerCaptor), any()) } private fun verifyNeverRequestsTriggerSensor() { verify(sensorManager, never()).requestTriggerSensor(any(), any()) } private fun verifyCancelTriggerSensor() { verify(sensorManager).cancelTriggerSensor(any(), any()) } } packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt +3 −5 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic Loading Loading @@ -52,11 +52,9 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { val testScope = kosmos.testScope val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository val primaryBouncerInteractor = kosmos.primaryBouncerInteractor val primaryBouncerInteractor = kosmos.mockPrimaryBouncerInteractor val sysuiStatusBarStateController = kosmos.sysuiStatusBarStateController val underTest by lazy { kosmos.primaryBouncerToGoneTransitionViewModel } val underTest by lazy { kosmos.primaryBouncerToGoneTransitionViewModel } @Before fun setUp() { Loading packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +0 −7 Original line number Diff line number Diff line Loading @@ -51,7 +51,6 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.ImmersiveModeConfirmation import com.android.systemui.statusbar.gesture.GesturePointerEventListener import com.android.systemui.statusbar.notification.InstantAppNotifier import com.android.systemui.statusbar.phone.KeyguardLiftController import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener import com.android.systemui.stylus.StylusUsiPowerStartable Loading Loading @@ -225,12 +224,6 @@ abstract class SystemUICoreStartableModule { @ClassKey(WMShell::class) abstract fun bindWMShell(sysui: WMShell): CoreStartable /** Inject into KeyguardLiftController. */ @Binds @IntoMap @ClassKey(KeyguardLiftController::class) abstract fun bindKeyguardLiftController(sysui: KeyguardLiftController): CoreStartable /** Inject into MediaTttSenderCoordinator. */ @Binds @IntoMap Loading packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt→packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinder.kt +143 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * 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. Loading @@ -11,112 +11,127 @@ * 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 * limitations under the License. */ package com.android.systemui.statusbar.phone package com.android.systemui.deviceentry.ui.binder import android.content.Context import android.content.pm.PackageManager import android.hardware.Sensor import android.hardware.TriggerEvent import android.hardware.TriggerEventListener import com.android.keyguard.ActiveUnlockConfig import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.CoreStartable import com.android.systemui.Dumpable import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.util.Assert import com.android.systemui.util.sensors.AsyncSensorManager import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch /** * Triggers face auth on lift when the device is showing the lock screen. Only initialized * if face auth is supported on the device. Not to be confused with the lift to wake gesture * which is handled by {@link com.android.server.policy.PhoneWindowManager}. * Triggers face auth and active unlock on lift when the device is showing the lock screen or * bouncer. Only initialized if face auth is supported on the device. Not to be confused with the * lift to wake gesture which is handled by {@link com.android.server.policy.PhoneWindowManager}. */ @SysUISingleton class KeyguardLiftController @Inject constructor( private val context: Context, private val statusBarStateController: StatusBarStateController, class LiftToRunFaceAuthBinder @Inject constructor( @Application private val scope: CoroutineScope, private val packageManager: PackageManager, private val asyncSensorManager: AsyncSensorManager, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, keyguardInteractor: KeyguardInteractor, primaryBouncerInteractor: PrimaryBouncerInteractor, alternateBouncerInteractor: AlternateBouncerInteractor, private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, private val dumpManager: DumpManager, private val selectedUserInteractor: SelectedUserInteractor, ) : Dumpable, CoreStartable { powerInteractor: PowerInteractor, ) : CoreStartable { private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) private var isListening = false private var bouncerVisible = false private var pickupSensor: Sensor? = null private val isListening: MutableStateFlow<Boolean> = MutableStateFlow(false) private val stoppedListening: Flow<Unit> = isListening.filterNot { it }.map {} // map to Unit private val onAwakeKeyguard: Flow<Boolean> = combine( powerInteractor.isInteractive, keyguardInteractor.isKeyguardVisible, ) { isInteractive, isKeyguardVisible -> isInteractive && isKeyguardVisible } private val bouncerShowing: Flow<Boolean> = combine( primaryBouncerInteractor.isShowing, alternateBouncerInteractor.isVisible, ) { primaryBouncerShowing, alternateBouncerShowing -> primaryBouncerShowing || alternateBouncerShowing } private val listenForPickupSensor: Flow<Boolean> = combine( stoppedListening, bouncerShowing, onAwakeKeyguard, ) { _, bouncerShowing, onAwakeKeyguard -> (onAwakeKeyguard || bouncerShowing) && deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled() } override fun start() { if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { if (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { init() } } private fun init() { dumpManager.registerDumpable(this) statusBarStateController.addCallback(statusBarStateListener) keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) updateListeningState() pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) scope.launch { listenForPickupSensor.collect { listenForPickupSensor -> updateListeningState(listenForPickupSensor) } } } private val listener: TriggerEventListener = object : TriggerEventListener() { private val listener: TriggerEventListener = object : TriggerEventListener() { override fun onTrigger(event: TriggerEvent?) { Assert.isMainThread() // Not listening anymore since trigger events unregister themselves isListening = false updateListeningState() deviceEntryFaceAuthInteractor.onDeviceLifted() keyguardUpdateMonitor.requestActiveUnlock( ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE, "KeyguardLiftController") } } "KeyguardLiftController" ) private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { override fun onKeyguardBouncerFullyShowingChanged(bouncer: Boolean) { bouncerVisible = bouncer updateListeningState() } override fun onKeyguardVisibilityChanged(visible: Boolean) { updateListeningState() } } private val statusBarStateListener = object : StatusBarStateController.StateListener { override fun onDozingChanged(isDozing: Boolean) { updateListeningState() // Not listening anymore since trigger events unregister themselves isListening.value = false } } override fun dump(pw: PrintWriter, args: Array<out String>) { pw.println("KeyguardLiftController:") pw.println("LiftToRunFaceAuthBinder:") pw.println(" pickupSensor: $pickupSensor") pw.println(" isListening: $isListening") pw.println(" bouncerVisible: $bouncerVisible") pw.println(" isListening: ${isListening.value}") } private fun updateListeningState() { private fun updateListeningState(shouldListen: Boolean) { if (pickupSensor == null) { return } val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible && !statusBarStateController.isDozing val isFaceEnabled = deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled() val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled if (shouldListen != isListening) { isListening = shouldListen if (shouldListen != isListening.value) { isListening.value = shouldListen if (shouldListen) { asyncSensorManager.requestTriggerSensor(listener, pickupSensor) Loading packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +2 −2 Original line number Diff line number Diff line Loading @@ -51,7 +51,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager; import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule; import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule; import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthModule; import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule; Loading Loading @@ -104,7 +104,7 @@ import kotlinx.coroutines.CoroutineDispatcher; FalsingModule.class, KeyguardDataQuickAffordanceModule.class, KeyguardRepositoryModule.class, KeyguardFaceAuthModule.class, DeviceEntryFaceAuthModule.class, KeyguardDisplayModule.class, StartKeyguardTransitionModule.class, ResourceTrimmerModule.class, Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/ui/binder/LiftToRunFaceAuthBinderTest.kt 0 → 100644 +200 −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.ui.binder import android.content.packageManager import android.content.pm.PackageManager import android.hardware.Sensor import android.hardware.TriggerEventListener import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.deviceentry.ui.binder.liftToRunFaceAuthBinder import com.android.systemui.keyguard.data.repository.biometricSettingsRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.kosmos.testScope import com.android.systemui.power.data.repository.fakePowerRepository import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.whenever import com.android.systemui.util.sensors.asyncSensorManager import kotlinx.coroutines.ExperimentalCoroutinesApi 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.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @ExperimentalCoroutinesApi @SmallTest @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper(setAsMainLooper = true) class LiftToRunFaceAuthBinderTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val sensorManager = kosmos.asyncSensorManager private val powerRepository = kosmos.fakePowerRepository private val keyguardRepository = kosmos.fakeKeyguardRepository private val bouncerRepository = kosmos.keyguardBouncerRepository private val biometricSettingsRepository = kosmos.biometricSettingsRepository private val packageManager = kosmos.packageManager @Captor private lateinit var triggerEventListenerCaptor: ArgumentCaptor<TriggerEventListener> @Mock private lateinit var mockSensor: Sensor private val underTest = kosmos.liftToRunFaceAuthBinder @Before fun setup() { MockitoAnnotations.initMocks(this) whenever(packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true) whenever(sensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)).thenReturn(mockSensor) } @Test fun doNotListenForGesture() = testScope.runTest { start() verifyNeverRequestsTriggerSensor() } @Test fun awakeKeyguard_listenForGesture() = testScope.runTest { start() givenAwakeKeyguard(true) runCurrent() verifyRequestTriggerSensor() } @Test fun faceNotEnrolled_listenForGesture() = testScope.runTest { start() givenAwakeKeyguard(true) biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(false) runCurrent() verifyNeverRequestsTriggerSensor() } @Test fun notInteractive_doNotListenForGesture() = testScope.runTest { start() givenAwakeKeyguard(true) powerRepository.setInteractive(false) runCurrent() verifyNeverRequestsTriggerSensor() } @Test fun primaryBouncer_listenForGesture() = testScope.runTest { start() givenAwakeKeyguard(false) givenPrimaryBouncerShowing() runCurrent() verifyRequestTriggerSensor() } @Test fun alternateBouncer_listenForGesture() = testScope.runTest { start() givenAwakeKeyguard(false) givenAlternateBouncerShowing() runCurrent() verifyRequestTriggerSensor() } @Test fun restartListeningForGestureAfterSensorTrigger() = testScope.runTest { start() givenAwakeKeyguard(true) runCurrent() verifyRequestTriggerSensor() clearInvocations(sensorManager) triggerEventListenerCaptor.value.onTrigger(null) runCurrent() verifyRequestTriggerSensor() } @Test fun cancelTriggerSensor_keyguardNotAwakeAnymore() = testScope.runTest { start() givenAwakeKeyguard(true) runCurrent() verifyRequestTriggerSensor() givenAwakeKeyguard(false) runCurrent() verifyCancelTriggerSensor() } private fun start() { underTest.start() biometricSettingsRepository.setIsFaceAuthEnrolledAndEnabled(true) givenAwakeKeyguard(false) givenBouncerNotShowing() } private fun givenAwakeKeyguard(isAwake: Boolean) { powerRepository.setInteractive(isAwake) keyguardRepository.setKeyguardShowing(isAwake) keyguardRepository.setKeyguardOccluded(false) } private fun givenPrimaryBouncerShowing() { bouncerRepository.setPrimaryShow(true) bouncerRepository.setAlternateVisible(false) } private fun givenBouncerNotShowing() { bouncerRepository.setPrimaryShow(false) bouncerRepository.setAlternateVisible(false) } private fun givenAlternateBouncerShowing() { bouncerRepository.setPrimaryShow(false) bouncerRepository.setAlternateVisible(true) } private fun verifyRequestTriggerSensor() { verify(sensorManager).requestTriggerSensor(capture(triggerEventListenerCaptor), any()) } private fun verifyNeverRequestsTriggerSensor() { verify(sensorManager, never()).requestTriggerSensor(any(), any()) } private fun verifyCancelTriggerSensor() { verify(sensorManager).cancelTriggerSensor(any(), any()) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt +3 −5 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.Flags import com.android.systemui.flags.fakeFeatureFlagsClassic Loading Loading @@ -52,11 +52,9 @@ class PrimaryBouncerToGoneTransitionViewModelTest : SysuiTestCase() { val testScope = kosmos.testScope val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository val primaryBouncerInteractor = kosmos.primaryBouncerInteractor val primaryBouncerInteractor = kosmos.mockPrimaryBouncerInteractor val sysuiStatusBarStateController = kosmos.sysuiStatusBarStateController val underTest by lazy { kosmos.primaryBouncerToGoneTransitionViewModel } val underTest by lazy { kosmos.primaryBouncerToGoneTransitionViewModel } @Before fun setUp() { Loading
packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +0 −7 Original line number Diff line number Diff line Loading @@ -51,7 +51,6 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.ImmersiveModeConfirmation import com.android.systemui.statusbar.gesture.GesturePointerEventListener import com.android.systemui.statusbar.notification.InstantAppNotifier import com.android.systemui.statusbar.phone.KeyguardLiftController import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener import com.android.systemui.stylus.StylusUsiPowerStartable Loading Loading @@ -225,12 +224,6 @@ abstract class SystemUICoreStartableModule { @ClassKey(WMShell::class) abstract fun bindWMShell(sysui: WMShell): CoreStartable /** Inject into KeyguardLiftController. */ @Binds @IntoMap @ClassKey(KeyguardLiftController::class) abstract fun bindKeyguardLiftController(sysui: KeyguardLiftController): CoreStartable /** Inject into MediaTttSenderCoordinator. */ @Binds @IntoMap Loading
packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt→packages/SystemUI/src/com/android/systemui/deviceentry/ui/binder/LiftToRunFaceAuthBinder.kt +143 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * 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. Loading @@ -11,112 +11,127 @@ * 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 * limitations under the License. */ package com.android.systemui.statusbar.phone package com.android.systemui.deviceentry.ui.binder import android.content.Context import android.content.pm.PackageManager import android.hardware.Sensor import android.hardware.TriggerEvent import android.hardware.TriggerEventListener import com.android.keyguard.ActiveUnlockConfig import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.CoreStartable import com.android.systemui.Dumpable import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.util.Assert import com.android.systemui.util.sensors.AsyncSensorManager import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch /** * Triggers face auth on lift when the device is showing the lock screen. Only initialized * if face auth is supported on the device. Not to be confused with the lift to wake gesture * which is handled by {@link com.android.server.policy.PhoneWindowManager}. * Triggers face auth and active unlock on lift when the device is showing the lock screen or * bouncer. Only initialized if face auth is supported on the device. Not to be confused with the * lift to wake gesture which is handled by {@link com.android.server.policy.PhoneWindowManager}. */ @SysUISingleton class KeyguardLiftController @Inject constructor( private val context: Context, private val statusBarStateController: StatusBarStateController, class LiftToRunFaceAuthBinder @Inject constructor( @Application private val scope: CoroutineScope, private val packageManager: PackageManager, private val asyncSensorManager: AsyncSensorManager, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, keyguardInteractor: KeyguardInteractor, primaryBouncerInteractor: PrimaryBouncerInteractor, alternateBouncerInteractor: AlternateBouncerInteractor, private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, private val dumpManager: DumpManager, private val selectedUserInteractor: SelectedUserInteractor, ) : Dumpable, CoreStartable { powerInteractor: PowerInteractor, ) : CoreStartable { private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) private var isListening = false private var bouncerVisible = false private var pickupSensor: Sensor? = null private val isListening: MutableStateFlow<Boolean> = MutableStateFlow(false) private val stoppedListening: Flow<Unit> = isListening.filterNot { it }.map {} // map to Unit private val onAwakeKeyguard: Flow<Boolean> = combine( powerInteractor.isInteractive, keyguardInteractor.isKeyguardVisible, ) { isInteractive, isKeyguardVisible -> isInteractive && isKeyguardVisible } private val bouncerShowing: Flow<Boolean> = combine( primaryBouncerInteractor.isShowing, alternateBouncerInteractor.isVisible, ) { primaryBouncerShowing, alternateBouncerShowing -> primaryBouncerShowing || alternateBouncerShowing } private val listenForPickupSensor: Flow<Boolean> = combine( stoppedListening, bouncerShowing, onAwakeKeyguard, ) { _, bouncerShowing, onAwakeKeyguard -> (onAwakeKeyguard || bouncerShowing) && deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled() } override fun start() { if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { if (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { init() } } private fun init() { dumpManager.registerDumpable(this) statusBarStateController.addCallback(statusBarStateListener) keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) updateListeningState() pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) scope.launch { listenForPickupSensor.collect { listenForPickupSensor -> updateListeningState(listenForPickupSensor) } } } private val listener: TriggerEventListener = object : TriggerEventListener() { private val listener: TriggerEventListener = object : TriggerEventListener() { override fun onTrigger(event: TriggerEvent?) { Assert.isMainThread() // Not listening anymore since trigger events unregister themselves isListening = false updateListeningState() deviceEntryFaceAuthInteractor.onDeviceLifted() keyguardUpdateMonitor.requestActiveUnlock( ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE, "KeyguardLiftController") } } "KeyguardLiftController" ) private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { override fun onKeyguardBouncerFullyShowingChanged(bouncer: Boolean) { bouncerVisible = bouncer updateListeningState() } override fun onKeyguardVisibilityChanged(visible: Boolean) { updateListeningState() } } private val statusBarStateListener = object : StatusBarStateController.StateListener { override fun onDozingChanged(isDozing: Boolean) { updateListeningState() // Not listening anymore since trigger events unregister themselves isListening.value = false } } override fun dump(pw: PrintWriter, args: Array<out String>) { pw.println("KeyguardLiftController:") pw.println("LiftToRunFaceAuthBinder:") pw.println(" pickupSensor: $pickupSensor") pw.println(" isListening: $isListening") pw.println(" bouncerVisible: $bouncerVisible") pw.println(" isListening: ${isListening.value}") } private fun updateListeningState() { private fun updateListeningState(shouldListen: Boolean) { if (pickupSensor == null) { return } val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible && !statusBarStateController.isDozing val isFaceEnabled = deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled() val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled if (shouldListen != isListening) { isListening = shouldListen if (shouldListen != isListening.value) { isListening.value = shouldListen if (shouldListen) { asyncSensorManager.requestTriggerSensor(listener, pickupSensor) Loading
packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +2 −2 Original line number Diff line number Diff line Loading @@ -51,7 +51,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WindowManagerLockscreenVisibilityManager; import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule; import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule; import com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthModule; import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule; Loading Loading @@ -104,7 +104,7 @@ import kotlinx.coroutines.CoroutineDispatcher; FalsingModule.class, KeyguardDataQuickAffordanceModule.class, KeyguardRepositoryModule.class, KeyguardFaceAuthModule.class, DeviceEntryFaceAuthModule.class, KeyguardDisplayModule.class, StartKeyguardTransitionModule.class, ResourceTrimmerModule.class, Loading