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.annotation.IntDef
20 import android.content.Context
21 import android.content.pm.PackageManager
22 import android.hardware.biometrics.BiometricSourceType
23 import android.provider.Settings
24 import com.android.systemui.Dumpable
25 import com.android.systemui.R
26 import com.android.systemui.dagger.SysUISingleton
27 import com.android.systemui.dump.DumpManager
28 import com.android.systemui.plugins.statusbar.StatusBarStateController
29 import com.android.systemui.statusbar.NotificationLockscreenUserManager
30 import com.android.systemui.statusbar.StatusBarState
31 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm
32 import com.android.systemui.statusbar.policy.KeyguardStateController
33 import com.android.systemui.tuner.TunerService
34 import java.io.FileDescriptor
35 import java.io.PrintWriter
36 import javax.inject.Inject
37 
38 @SysUISingleton
39 open class KeyguardBypassController : Dumpable, StackScrollAlgorithm.BypassController {
40 
41     private val mKeyguardStateController: KeyguardStateController
42     private val statusBarStateController: StatusBarStateController
43     @BypassOverride private val bypassOverride: Int
44     private var hasFaceFeature: Boolean
45     private var pendingUnlock: PendingUnlock? = null
46     private val listeners = mutableListOf<OnBypassStateChangedListener>()
47 
48     private val faceAuthEnabledChangedCallback = object : KeyguardStateController.Callback {
49         override fun onFaceAuthEnabledChanged() = notifyListeners()
50     }
51     var userHasDeviceEntryIntent: Boolean = false // ie: attempted udfps auth
52 
53     @IntDef(
54         FACE_UNLOCK_BYPASS_NO_OVERRIDE,
55         FACE_UNLOCK_BYPASS_ALWAYS,
56         FACE_UNLOCK_BYPASS_NEVER
57     )
58     @Retention(AnnotationRetention.SOURCE)
59     private annotation class BypassOverride
60 
61     /**
62      * Pending unlock info:
63      *
64      * The pending unlock type which is set if the bypass was blocked when it happened.
65      *
66      * Whether the pending unlock type is strong biometric or non-strong biometric
67      * (i.e. weak or convenience).
68      */
69     private data class PendingUnlock(
70         val pendingUnlockType: BiometricSourceType,
71         val isStrongBiometric: Boolean
72     )
73 
74     lateinit var unlockController: BiometricUnlockController
75     var isPulseExpanding = false
76 
77     /** delegates to [bypassEnabled] but conforms to [StackScrollAlgorithm.BypassController] */
78     override fun isBypassEnabled() = bypassEnabled
79 
80     /**
81      * If face unlock dismisses the lock screen or keeps user on keyguard for the current user.
82      */
83     var bypassEnabled: Boolean = false
84         get() {
85             val enabled = when (bypassOverride) {
86                 FACE_UNLOCK_BYPASS_ALWAYS -> true
87                 FACE_UNLOCK_BYPASS_NEVER -> false
88                 else -> field
89             }
90             return enabled && mKeyguardStateController.isFaceAuthEnabled
91         }
92         private set(value) {
93             field = value
94             notifyListeners()
95         }
96 
97     var bouncerShowing: Boolean = false
98     var altBouncerShowing: Boolean = false
99     var launchingAffordance: Boolean = false
100     var qSExpanded = false
101         set(value) {
102             val changed = field != value
103             field = value
104             if (changed && !value) {
105                 maybePerformPendingUnlock()
106             }
107         }
108 
109     @Inject
110     constructor(
111         context: Context,
112         tunerService: TunerService,
113         statusBarStateController: StatusBarStateController,
114         lockscreenUserManager: NotificationLockscreenUserManager,
115         keyguardStateController: KeyguardStateController,
116         dumpManager: DumpManager
117     ) {
118         this.mKeyguardStateController = keyguardStateController
119         this.statusBarStateController = statusBarStateController
120 
121         bypassOverride = context.resources.getInteger(R.integer.config_face_unlock_bypass_override)
122 
123         hasFaceFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)
124         if (!hasFaceFeature) {
125             return
126         }
127 
128         dumpManager.registerDumpable("KeyguardBypassController", this)
129         statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
130             override fun onStateChanged(newState: Int) {
131                 if (newState != StatusBarState.KEYGUARD) {
132                     pendingUnlock = null
133                 }
134             }
135         })
136 
137         val dismissByDefault = if (context.resources.getBoolean(
138                         com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0
139         tunerService.addTunable(object : TunerService.Tunable {
140             override fun onTuningChanged(key: String?, newValue: String?) {
141                 bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
142             }
143         }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
144         lockscreenUserManager.addUserChangedListener(
145                 object : NotificationLockscreenUserManager.UserChangedListener {
146                     override fun onUserChanged(userId: Int) {
147                         pendingUnlock = null
148                     }
149                 })
150     }
151 
152     private fun notifyListeners() = listeners.forEach { it.onBypassStateChanged(bypassEnabled) }
153 
154     /**
155      * Notify that the biometric unlock has happened.
156      *
157      * @return false if we can not wake and unlock right now
158      */
159     fun onBiometricAuthenticated(
160         biometricSourceType: BiometricSourceType,
161         isStrongBiometric: Boolean
162     ): Boolean {
163         if (biometricSourceType == BiometricSourceType.FACE && bypassEnabled) {
164             val can = canBypass()
165             if (!can && (isPulseExpanding || qSExpanded)) {
166                 pendingUnlock = PendingUnlock(biometricSourceType, isStrongBiometric)
167             }
168             return can
169         }
170         return true
171     }
172 
173     fun maybePerformPendingUnlock() {
174         if (pendingUnlock != null) {
175             if (onBiometricAuthenticated(pendingUnlock!!.pendingUnlockType,
176                             pendingUnlock!!.isStrongBiometric)) {
177                 unlockController.startWakeAndUnlock(pendingUnlock!!.pendingUnlockType,
178                         pendingUnlock!!.isStrongBiometric)
179                 pendingUnlock = null
180             }
181         }
182     }
183 
184     /**
185      * If keyguard can be dismissed because of bypass.
186      */
187     fun canBypass(): Boolean {
188         if (bypassEnabled) {
189             return when {
190                 bouncerShowing -> true
191                 altBouncerShowing -> true
192                 statusBarStateController.state != StatusBarState.KEYGUARD -> false
193                 launchingAffordance -> false
194                 isPulseExpanding || qSExpanded -> false
195                 else -> true
196             }
197         }
198         return false
199     }
200 
201     /**
202      * If shorter animations should be played when unlocking.
203      */
204     fun canPlaySubtleWindowAnimations(): Boolean {
205         if (bypassEnabled) {
206             return when {
207                 statusBarStateController.state != StatusBarState.KEYGUARD -> false
208                 qSExpanded -> false
209                 else -> true
210             }
211         }
212         return false
213     }
214 
215     fun onStartedGoingToSleep() {
216         pendingUnlock = null
217     }
218 
219     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
220         pw.println("KeyguardBypassController:")
221         if (pendingUnlock != null) {
222             pw.println("  mPendingUnlock.pendingUnlockType: ${pendingUnlock!!.pendingUnlockType}")
223             pw.println("  mPendingUnlock.isStrongBiometric: ${pendingUnlock!!.isStrongBiometric}")
224         } else {
225             pw.println("  mPendingUnlock: $pendingUnlock")
226         }
227         pw.println("  bypassEnabled: $bypassEnabled")
228         pw.println("  canBypass: ${canBypass()}")
229         pw.println("  bouncerShowing: $bouncerShowing")
230         pw.println("  altBouncerShowing: $altBouncerShowing")
231         pw.println("  isPulseExpanding: $isPulseExpanding")
232         pw.println("  launchingAffordance: $launchingAffordance")
233         pw.println("  qSExpanded: $qSExpanded")
234         pw.println("  hasFaceFeature: $hasFaceFeature")
235         pw.println("  userHasDeviceEntryIntent: $userHasDeviceEntryIntent")
236     }
237 
238     /** Registers a listener for bypass state changes. */
239     fun registerOnBypassStateChangedListener(listener: OnBypassStateChangedListener) {
240         val start = listeners.isEmpty()
241         listeners.add(listener)
242         if (start) {
243             mKeyguardStateController.addCallback(faceAuthEnabledChangedCallback)
244         }
245     }
246 
247     /**
248      * Unregisters a listener for bypass state changes, previous registered with
249      * [registerOnBypassStateChangedListener]
250      */
251     fun unregisterOnBypassStateChangedListener(listener: OnBypassStateChangedListener) {
252         listeners.remove(listener)
253         if (listeners.isEmpty()) {
254             mKeyguardStateController.removeCallback(faceAuthEnabledChangedCallback)
255         }
256     }
257 
258     /** Listener for bypass state change events.  */
259     interface OnBypassStateChangedListener {
260         /** Invoked when bypass becomes enabled or disabled. */
261         fun onBypassStateChanged(isEnabled: Boolean)
262     }
263 
264     companion object {
265         const val BYPASS_FADE_DURATION = 67
266 
267         private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
268         private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
269         private const val FACE_UNLOCK_BYPASS_NEVER = 2
270     }
271 }
272