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