1 /*
2  * Copyright (C) 2019 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.statusbar.phone
18 
19 import android.content.Context
20 import android.content.pm.PackageManager
21 import android.hardware.Sensor
22 import android.hardware.TriggerEvent
23 import android.hardware.TriggerEventListener
24 import com.android.keyguard.ActiveUnlockConfig
25 import com.android.keyguard.FaceAuthApiRequestReason
26 import com.android.keyguard.KeyguardUpdateMonitor
27 import com.android.keyguard.KeyguardUpdateMonitorCallback
28 import com.android.systemui.CoreStartable
29 import com.android.systemui.Dumpable
30 import com.android.systemui.dagger.SysUISingleton
31 import com.android.systemui.dump.DumpManager
32 import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor
33 import com.android.systemui.plugins.statusbar.StatusBarStateController
34 import com.android.systemui.util.Assert
35 import com.android.systemui.util.sensors.AsyncSensorManager
36 import java.io.PrintWriter
37 import javax.inject.Inject
38 
39 /**
40  * Triggers face auth on lift when the device is showing the lock screen. Only initialized
41  * if face auth is supported on the device. Not to be confused with the lift to wake gesture
42  * which is handled by {@link com.android.server.policy.PhoneWindowManager}.
43  */
44 @SysUISingleton
45 class KeyguardLiftController @Inject constructor(
46     private val context: Context,
47     private val statusBarStateController: StatusBarStateController,
48     private val asyncSensorManager: AsyncSensorManager,
49     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
50     private val keyguardFaceAuthInteractor: KeyguardFaceAuthInteractor,
51     private val dumpManager: DumpManager
52 ) : Dumpable, CoreStartable {
53 
54     private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)
55     private var isListening = false
56     private var bouncerVisible = false
57 
58     override fun start() {
59         if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
60             init()
61         }
62     }
63 
64     private fun init() {
65         dumpManager.registerDumpable(javaClass.name, this)
66         statusBarStateController.addCallback(statusBarStateListener)
67         keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
68         updateListeningState()
69     }
70 
71     private val listener: TriggerEventListener = object : TriggerEventListener() {
72         override fun onTrigger(event: TriggerEvent?) {
73             Assert.isMainThread()
74             // Not listening anymore since trigger events unregister themselves
75             isListening = false
76             updateListeningState()
77             keyguardFaceAuthInteractor.onDeviceLifted()
78             keyguardUpdateMonitor.requestFaceAuth(
79                 FaceAuthApiRequestReason.PICK_UP_GESTURE_TRIGGERED
80             )
81             keyguardUpdateMonitor.requestActiveUnlock(
82                 ActiveUnlockConfig.ActiveUnlockRequestOrigin.WAKE,
83                 "KeyguardLiftController")
84         }
85     }
86 
87     private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
88         override fun onKeyguardBouncerFullyShowingChanged(bouncer: Boolean) {
89             bouncerVisible = bouncer
90             updateListeningState()
91         }
92 
93         override fun onKeyguardVisibilityChanged(visible: Boolean) {
94             updateListeningState()
95         }
96     }
97 
98     private val statusBarStateListener = object : StatusBarStateController.StateListener {
99         override fun onDozingChanged(isDozing: Boolean) {
100             updateListeningState()
101         }
102     }
103 
104     override fun dump(pw: PrintWriter, args: Array<out String>) {
105         pw.println("KeyguardLiftController:")
106         pw.println("  pickupSensor: $pickupSensor")
107         pw.println("  isListening: $isListening")
108         pw.println("  bouncerVisible: $bouncerVisible")
109     }
110 
111     private fun updateListeningState() {
112         if (pickupSensor == null) {
113             return
114         }
115         val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
116                 !statusBarStateController.isDozing
117 
118         val userId = KeyguardUpdateMonitor.getCurrentUser()
119         val isFaceEnabled = keyguardUpdateMonitor.isFaceAuthEnabledForUser(userId)
120         val shouldListen = (onKeyguard || bouncerVisible) && isFaceEnabled
121         if (shouldListen != isListening) {
122             isListening = shouldListen
123 
124             if (shouldListen) {
125                 asyncSensorManager.requestTriggerSensor(listener, pickupSensor)
126             } else {
127                 asyncSensorManager.cancelTriggerSensor(listener, pickupSensor)
128             }
129         }
130     }
131 }
132