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