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 com.android.app.tracing.coroutines.launchTraced as 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