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 var userHasDeviceEntryIntent: Boolean = false // ie: attempted udfps auth 47 48 @IntDef( 49 FACE_UNLOCK_BYPASS_NO_OVERRIDE, 50 FACE_UNLOCK_BYPASS_ALWAYS, 51 FACE_UNLOCK_BYPASS_NEVER 52 ) 53 @Retention(AnnotationRetention.SOURCE) 54 private annotation class BypassOverride 55 56 /** 57 * Pending unlock info: 58 * 59 * The pending unlock type which is set if the bypass was blocked when it happened. 60 * 61 * Whether the pending unlock type is strong biometric or non-strong biometric 62 * (i.e. weak or convenience). 63 */ 64 private data class PendingUnlock( 65 val pendingUnlockType: BiometricSourceType, 66 val isStrongBiometric: Boolean 67 ) 68 69 lateinit var unlockController: BiometricUnlockController 70 var isPulseExpanding = false 71 72 /** delegates to [bypassEnabled] but conforms to [StackScrollAlgorithm.BypassController] */ isBypassEnablednull73 override fun isBypassEnabled() = bypassEnabled 74 75 /** 76 * If face unlock dismisses the lock screen or keeps user on keyguard for the current user. 77 */ 78 var bypassEnabled: Boolean = false 79 get() { 80 val enabled = when (bypassOverride) { 81 FACE_UNLOCK_BYPASS_ALWAYS -> true 82 FACE_UNLOCK_BYPASS_NEVER -> false 83 else -> field 84 } 85 return enabled && mKeyguardStateController.isFaceAuthEnabled 86 } 87 private set 88 89 var bouncerShowing: Boolean = false 90 var altBouncerShowing: Boolean = false 91 var launchingAffordance: Boolean = false 92 var qSExpanded = false 93 set(value) { 94 val changed = field != value 95 field = value 96 if (changed && !value) { 97 maybePerformPendingUnlock() 98 } 99 } 100 101 @Inject 102 constructor( 103 context: Context, 104 tunerService: TunerService, 105 statusBarStateController: StatusBarStateController, 106 lockscreenUserManager: NotificationLockscreenUserManager, 107 keyguardStateController: KeyguardStateController, 108 dumpManager: DumpManager 109 ) { 110 this.mKeyguardStateController = keyguardStateController 111 this.statusBarStateController = statusBarStateController 112 113 bypassOverride = context.resources.getInteger(R.integer.config_face_unlock_bypass_override) 114 115 hasFaceFeature = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE) 116 if (!hasFaceFeature) { 117 return 118 } 119 120 dumpManager.registerDumpable("KeyguardBypassController", this) 121 statusBarStateController.addCallback(object : StatusBarStateController.StateListener { onStateChangednull122 override fun onStateChanged(newState: Int) { 123 if (newState != StatusBarState.KEYGUARD) { 124 pendingUnlock = null 125 } 126 } 127 }) 128 129 val dismissByDefault = if (context.resources.getBoolean( 130 com.android.internal.R.bool.config_faceAuthDismissesKeyguard)) 1 else 0 131 tunerService.addTunable(object : TunerService.Tunable { onTuningChangednull132 override fun onTuningChanged(key: String?, newValue: String?) { 133 bypassEnabled = tunerService.getValue(key, dismissByDefault) != 0 134 } 135 }, Settings.Secure.FACE_UNLOCK_DISMISSES_KEYGUARD) 136 lockscreenUserManager.addUserChangedListener( 137 object : NotificationLockscreenUserManager.UserChangedListener { onUserChangednull138 override fun onUserChanged(userId: Int) { 139 pendingUnlock = null 140 } 141 }) 142 } 143 144 /** 145 * Notify that the biometric unlock has happened. 146 * 147 * @return false if we can not wake and unlock right now 148 */ onBiometricAuthenticatednull149 fun onBiometricAuthenticated( 150 biometricSourceType: BiometricSourceType, 151 isStrongBiometric: Boolean 152 ): Boolean { 153 if (biometricSourceType == BiometricSourceType.FACE && bypassEnabled) { 154 val can = canBypass() 155 if (!can && (isPulseExpanding || qSExpanded)) { 156 pendingUnlock = PendingUnlock(biometricSourceType, isStrongBiometric) 157 } 158 return can 159 } 160 return true 161 } 162 maybePerformPendingUnlocknull163 fun maybePerformPendingUnlock() { 164 if (pendingUnlock != null) { 165 if (onBiometricAuthenticated(pendingUnlock!!.pendingUnlockType, 166 pendingUnlock!!.isStrongBiometric)) { 167 unlockController.startWakeAndUnlock(pendingUnlock!!.pendingUnlockType, 168 pendingUnlock!!.isStrongBiometric) 169 pendingUnlock = null 170 } 171 } 172 } 173 174 /** 175 * If keyguard can be dismissed because of bypass. 176 */ canBypassnull177 fun canBypass(): Boolean { 178 if (bypassEnabled) { 179 return when { 180 bouncerShowing -> true 181 altBouncerShowing -> true 182 statusBarStateController.state != StatusBarState.KEYGUARD -> false 183 launchingAffordance -> false 184 isPulseExpanding || qSExpanded -> false 185 else -> true 186 } 187 } 188 return false 189 } 190 191 /** 192 * If shorter animations should be played when unlocking. 193 */ canPlaySubtleWindowAnimationsnull194 fun canPlaySubtleWindowAnimations(): Boolean { 195 if (bypassEnabled) { 196 return when { 197 statusBarStateController.state != StatusBarState.KEYGUARD -> false 198 qSExpanded -> false 199 else -> true 200 } 201 } 202 return false 203 } 204 onStartedGoingToSleepnull205 fun onStartedGoingToSleep() { 206 pendingUnlock = null 207 } 208 dumpnull209 override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { 210 pw.println("KeyguardBypassController:") 211 if (pendingUnlock != null) { 212 pw.println(" mPendingUnlock.pendingUnlockType: ${pendingUnlock!!.pendingUnlockType}") 213 pw.println(" mPendingUnlock.isStrongBiometric: ${pendingUnlock!!.isStrongBiometric}") 214 } else { 215 pw.println(" mPendingUnlock: $pendingUnlock") 216 } 217 pw.println(" bypassEnabled: $bypassEnabled") 218 pw.println(" canBypass: ${canBypass()}") 219 pw.println(" bouncerShowing: $bouncerShowing") 220 pw.println(" altBouncerShowing: $altBouncerShowing") 221 pw.println(" isPulseExpanding: $isPulseExpanding") 222 pw.println(" launchingAffordance: $launchingAffordance") 223 pw.println(" qSExpanded: $qSExpanded") 224 pw.println(" hasFaceFeature: $hasFaceFeature") 225 pw.println(" userHasDeviceEntryIntent: $userHasDeviceEntryIntent") 226 } 227 228 companion object { 229 const val BYPASS_FADE_DURATION = 67 230 231 private const val FACE_UNLOCK_BYPASS_NO_OVERRIDE = 0 232 private const val FACE_UNLOCK_BYPASS_ALWAYS = 1 233 private const val FACE_UNLOCK_BYPASS_NEVER = 2 234 } 235 } 236