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