1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.deviceentry.ui.binder 18 19 import android.content.pm.PackageManager 20 import android.hardware.Sensor 21 import android.hardware.TriggerEvent 22 import android.hardware.TriggerEventListener 23 import com.android.keyguard.ActiveUnlockConfig 24 import com.android.keyguard.KeyguardUpdateMonitor 25 import com.android.systemui.CoreStartable 26 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor 27 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor 28 import com.android.systemui.dagger.SysUISingleton 29 import com.android.systemui.dagger.qualifiers.Application 30 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor 31 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor 32 import com.android.systemui.power.domain.interactor.PowerInteractor 33 import com.android.systemui.util.Assert 34 import com.android.systemui.util.sensors.AsyncSensorManager 35 import java.io.PrintWriter 36 import javax.inject.Inject 37 import kotlinx.coroutines.CoroutineScope 38 import kotlinx.coroutines.flow.Flow 39 import kotlinx.coroutines.flow.MutableStateFlow 40 import kotlinx.coroutines.flow.combine 41 import kotlinx.coroutines.flow.filterNot 42 import kotlinx.coroutines.flow.map 43 import kotlinx.coroutines.launch 44 45 /** 46 * Triggers face auth and active unlock on lift when the device is showing the lock screen or 47 * bouncer. Only initialized if face auth is supported on the device. Not to be confused with the 48 * lift to wake gesture which is handled by {@link com.android.server.policy.PhoneWindowManager}. 49 */ 50 @SysUISingleton 51 class LiftToRunFaceAuthBinder 52 @Inject 53 constructor( 54 @Application private val scope: CoroutineScope, 55 private val packageManager: PackageManager, 56 private val asyncSensorManager: AsyncSensorManager, 57 private val keyguardUpdateMonitor: KeyguardUpdateMonitor, 58 keyguardInteractor: KeyguardInteractor, 59 primaryBouncerInteractor: PrimaryBouncerInteractor, 60 alternateBouncerInteractor: AlternateBouncerInteractor, 61 private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, 62 powerInteractor: PowerInteractor, 63 ) : CoreStartable { 64 65 private var pickupSensor: Sensor? = null 66 private val isListening: MutableStateFlow<Boolean> = MutableStateFlow(false) 67 private val stoppedListening: Flow<Unit> = isListening.filterNot { it }.map {} // map to Unit 68 69 private val onAwakeKeyguard: Flow<Boolean> = 70 combine( 71 powerInteractor.isInteractive, 72 keyguardInteractor.isKeyguardVisible, 73 ) { isInteractive, isKeyguardVisible -> 74 isInteractive && isKeyguardVisible 75 } 76 private val bouncerShowing: Flow<Boolean> = 77 combine( 78 primaryBouncerInteractor.isShowing, 79 alternateBouncerInteractor.isVisible, 80 ) { primaryBouncerShowing, alternateBouncerShowing -> 81 primaryBouncerShowing || alternateBouncerShowing 82 } 83 private val listenForPickupSensor: Flow<Boolean> = 84 combine( 85 stoppedListening, 86 bouncerShowing, 87 onAwakeKeyguard, 88 ) { _, bouncerShowing, onAwakeKeyguard -> 89 (onAwakeKeyguard || bouncerShowing) && 90 deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled() 91 } 92 93 override fun start() { 94 if (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { 95 init() 96 } 97 } 98 99 private fun init() { 100 pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) 101 scope.launch { 102 listenForPickupSensor.collect { listenForPickupSensor -> 103 updateListeningState(listenForPickupSensor) 104 } 105 } 106 } 107 108 private val listener: TriggerEventListener = 109 object : TriggerEventListener() { 110 override fun onTrigger(event: TriggerEvent?) { 111 Assert.isMainThread() 112 deviceEntryFaceAuthInteractor.onDeviceLifted() 113 keyguardUpdateMonitor.requestActiveUnlock( 114 ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE, 115 "KeyguardLiftController" 116 ) 117 118 // Not listening anymore since trigger events unregister themselves 119 isListening.value = false 120 } 121 } 122 123 override fun dump(pw: PrintWriter, args: Array<out String>) { 124 pw.println("LiftToRunFaceAuthBinder:") 125 pw.println(" pickupSensor: $pickupSensor") 126 pw.println(" isListening: ${isListening.value}") 127 } 128 129 private fun updateListeningState(shouldListen: Boolean) { 130 if (pickupSensor == null) { 131 return 132 } 133 if (shouldListen != isListening.value) { 134 isListening.value = shouldListen 135 136 if (shouldListen) { 137 asyncSensorManager.requestTriggerSensor(listener, pickupSensor) 138 } else { 139 asyncSensorManager.cancelTriggerSensor(listener, pickupSensor) 140 } 141 } 142 } 143 } 144