• 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.pm.PackageManager
21 import android.content.res.Resources
22 import android.hardware.biometrics.BiometricSourceType
23 import android.provider.Settings
24 import com.android.app.tracing.ListenersTracing.forEachTraced
25 import com.android.app.tracing.coroutines.launchTraced as launch
26 import com.android.systemui.Dumpable
27 import com.android.systemui.dagger.SysUISingleton
28 import com.android.systemui.dagger.qualifiers.Application
29 import com.android.systemui.dagger.qualifiers.Main
30 import com.android.systemui.dump.DumpManager
31 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
32 import com.android.systemui.keyguard.shared.model.KeyguardState
33 import com.android.systemui.plugins.statusbar.StatusBarStateController
34 import com.android.systemui.res.R
35 import com.android.systemui.shade.domain.interactor.ShadeInteractor
36 import com.android.systemui.statusbar.NotificationLockscreenUserManager
37 import com.android.systemui.statusbar.StatusBarState
38 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm
39 import com.android.systemui.statusbar.policy.DevicePostureController
40 import com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_UNKNOWN
41 import com.android.systemui.statusbar.policy.DevicePostureController.DevicePostureInt
42 import com.android.systemui.statusbar.policy.KeyguardStateController
43 import com.android.systemui.tuner.TunerService
44 import dagger.Lazy
45 import java.io.PrintWriter
46 import javax.inject.Inject
47 import kotlinx.coroutines.CoroutineScope
48 import kotlinx.coroutines.flow.distinctUntilChanged
49 import kotlinx.coroutines.flow.map
50 import com.android.app.tracing.coroutines.launchTraced as launch
51 
52 @SysUISingleton
53 class KeyguardBypassController @Inject constructor(
54         @Main resources: Resources,
55         packageManager: PackageManager,
56         @Application private val applicationScope: CoroutineScope,
57         tunerService: TunerService,
58         private val statusBarStateController: StatusBarStateController,
59         lockscreenUserManager: NotificationLockscreenUserManager,
60         private val keyguardStateController: KeyguardStateController,
61         private val shadeInteractorLazy: Lazy<ShadeInteractor>,
62         devicePostureController: DevicePostureController,
63         private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
64         dumpManager: DumpManager
65 ) : Dumpable, StackScrollAlgorithm.BypassController {
66 
67     @BypassOverride private val bypassOverride: Int
68     private var hasFaceFeature: Boolean
69     @DevicePostureInt private val configFaceAuthSupportedPosture: Int
70     @DevicePostureInt private var postureState: Int = DEVICE_POSTURE_UNKNOWN
71     private var pendingUnlock: PendingUnlock? = null
72     private val listeners = mutableListOf<OnBypassStateChangedListener>()
73     private val faceAuthEnabledChangedCallback = object : KeyguardStateController.Callback {
74         override fun onFaceEnrolledChanged() = notifyListeners()
75     }
76 
77     @IntDef(
78         FACE_UNLOCK_BYPASS_NO_OVERRIDE,
79         FACE_UNLOCK_BYPASS_ALWAYS,
80         FACE_UNLOCK_BYPASS_NEVER
81     )
82     @Retention(AnnotationRetention.SOURCE)
83     private annotation class BypassOverride
84 
85     /**
86      * Pending unlock info:
87      *
88      * The pending unlock type which is set if the bypass was blocked when it happened.
89      *
90      * Whether the pending unlock type is strong biometric or non-strong biometric
91      * (i.e. weak or convenience).
92      */
93     private data class PendingUnlock(
94         val pendingUnlockType: BiometricSourceType,
95         val isStrongBiometric: Boolean
96     )
97 
98     lateinit var unlockController: BiometricUnlockController
99     var isPulseExpanding = false
100 
101     /** delegates to [bypassEnabled] but conforms to [StackScrollAlgorithm.BypassController] */
102     override fun isBypassEnabled() = bypassEnabled
103 
104     /**
105      * If face unlock dismisses the lock screen or keeps user on keyguard for the current user.
106      */
107     var bypassEnabled: Boolean = false
108         get() {
109             val enabled = when (bypassOverride) {
110                 FACE_UNLOCK_BYPASS_ALWAYS -> true
111                 FACE_UNLOCK_BYPASS_NEVER -> false
112                 else -> field
113             }
114             return enabled && keyguardStateController.isFaceEnrolledAndEnabled &&
115                     isPostureAllowedForFaceAuth()
116         }
117         private set(value) {
118             field = value
119             notifyListeners()
120         }
121 
122     var bouncerShowing: Boolean = false
123     var launchingAffordance: Boolean = false
124     var qsExpanded = false
125 
126     init {
127         bypassOverride = resources.getInteger(R.integer.config_face_unlock_bypass_override)
128         configFaceAuthSupportedPosture =
129             resources.getInteger(R.integer.config_face_auth_supported_posture)
130         hasFaceFeature = packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)
131         if (hasFaceFeature) {
132             if (configFaceAuthSupportedPosture != DEVICE_POSTURE_UNKNOWN) {
133                 devicePostureController.addCallback { posture ->
134                     if (postureState != posture) {
135                         postureState = posture
136                         notifyListeners()
137                     }
138                 }
139             }
140             dumpManager.registerNormalDumpable("KeyguardBypassController", this)
141             statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
142                 override fun onStateChanged(newState: Int) {
143                     if (newState != StatusBarState.KEYGUARD) {
144                         pendingUnlock = null
145                     }
146                 }
147             })
148             val dismissByDefault = if (resources.getBoolean(
149                             com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0
150             tunerService.addTunable({ key, _ ->
151                 bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0
152             }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD)
153             lockscreenUserManager.addUserChangedListener(
154                     object : NotificationLockscreenUserManager.UserChangedListener {
155                         override fun onUserChanged(userId: Int) {
156                             pendingUnlock = null
157                         }
158                     })
159         }
160     }
161 
162     fun listenForQsExpandedChange() =
163         applicationScope.launch("listenForQsExpandedChange") {
164             shadeInteractorLazy.get().qsExpansion.map { it > 0f }.distinctUntilChanged()
165                 .collect { isQsExpanded ->
166                     val changed = qsExpanded != isQsExpanded
167                     qsExpanded = isQsExpanded
168                     if (changed && !isQsExpanded) {
169                         maybePerformPendingUnlock()
170                     }
171                 }
172         }
173 
174     private fun notifyListeners() = listeners.forEachTraced("KeyguardBypassController") {
175         it.onBypassStateChanged(bypassEnabled)
176     }
177 
178     /**
179      * Notify that the biometric unlock has happened.
180      *
181      * @return false if we can not wake and unlock right now
182      */
183     fun onBiometricAuthenticated(
184         biometricSourceType: BiometricSourceType,
185         isStrongBiometric: Boolean
186     ): Boolean {
187         if (biometricSourceType == BiometricSourceType.FACE && bypassEnabled) {
188             val can = canBypass()
189             if (!can && (isPulseExpanding || qsExpanded)) {
190                 pendingUnlock = PendingUnlock(biometricSourceType, isStrongBiometric)
191             }
192             return can
193         }
194         return true
195     }
196 
197     fun maybePerformPendingUnlock() {
198         if (pendingUnlock != null) {
199             if (onBiometricAuthenticated(pendingUnlock!!.pendingUnlockType,
200                             pendingUnlock!!.isStrongBiometric)) {
201                 unlockController.startWakeAndUnlock(pendingUnlock!!.pendingUnlockType,
202                         pendingUnlock!!.isStrongBiometric)
203                 pendingUnlock = null
204             }
205         }
206     }
207 
208     /**
209      * If keyguard can be dismissed because of bypass.
210      */
211     fun canBypass(): Boolean {
212         if (bypassEnabled) {
213             return when {
214                 bouncerShowing -> true
215                 keyguardTransitionInteractor.getCurrentState() == KeyguardState.ALTERNATE_BOUNCER ->
216                     true
217                 statusBarStateController.state != StatusBarState.KEYGUARD -> false
218                 launchingAffordance -> false
219                 isPulseExpanding || qsExpanded -> false
220                 else -> true
221             }
222         }
223         return false
224     }
225 
226     fun onStartedGoingToSleep() {
227         pendingUnlock = null
228     }
229 
230     fun isPostureAllowedForFaceAuth(): Boolean {
231         return when (configFaceAuthSupportedPosture) {
232             DEVICE_POSTURE_UNKNOWN -> true
233             else -> (postureState == configFaceAuthSupportedPosture)
234         }
235     }
236 
237     override fun dump(pw: PrintWriter, args: Array<out String>) {
238         pw.println("KeyguardBypassController:")
239         if (pendingUnlock != null) {
240             pw.println("  mPendingUnlock.pendingUnlockType: ${pendingUnlock!!.pendingUnlockType}")
241             pw.println("  mPendingUnlock.isStrongBiometric: ${pendingUnlock!!.isStrongBiometric}")
242         } else {
243             pw.println("  mPendingUnlock: $pendingUnlock")
244         }
245         pw.println("  bypassEnabled: $bypassEnabled")
246         pw.println("  canBypass: ${canBypass()}")
247         pw.println("  bouncerShowing: $bouncerShowing")
248         pw.println("  altBouncerShowing:" +
249             " ${keyguardTransitionInteractor.getCurrentState() == KeyguardState.ALTERNATE_BOUNCER}")
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             keyguardStateController.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             keyguardStateController.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         private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0
285         private const val FACE_UNLOCK_BYPASS_ALWAYS = 1
286         private const val FACE_UNLOCK_BYPASS_NEVER = 2
287     }
288 }
289