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