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