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