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