1 /* 2 * Copyright (C) 2022 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.keyguard 18 19 import android.content.ContentResolver 20 import android.database.ContentObserver 21 import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT 22 import android.net.Uri 23 import android.os.Handler 24 import android.os.PowerManager 25 import android.os.PowerManager.WAKE_REASON_UNFOLD_DEVICE 26 import android.os.UserHandle 27 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL 28 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO 29 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS 30 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT 31 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED 32 import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE 33 import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS 34 import android.provider.Settings.Secure.ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD 35 import android.util.Log 36 import com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser 37 import com.android.systemui.Dumpable 38 import com.android.systemui.dagger.SysUISingleton 39 import com.android.systemui.dagger.qualifiers.Main 40 import com.android.systemui.dump.DumpManager 41 import com.android.systemui.util.settings.SecureSettings 42 import java.io.PrintWriter 43 import javax.inject.Inject 44 45 /** 46 * Handles active unlock settings changes. 47 */ 48 @SysUISingleton 49 class ActiveUnlockConfig @Inject constructor( 50 @Main private val handler: Handler, 51 private val secureSettings: SecureSettings, 52 private val contentResolver: ContentResolver, 53 dumpManager: DumpManager 54 ) : Dumpable { 55 56 companion object { 57 const val TAG = "ActiveUnlockConfig" 58 } 59 60 /** 61 * Indicates the origin for an active unlock request. 62 */ 63 enum class ActiveUnlockRequestOrigin { 64 /** 65 * Trigger ActiveUnlock on wake ups that'd trigger FaceAuth, see [FaceWakeUpTriggersConfig] 66 */ 67 WAKE, 68 69 /** 70 * Trigger ActiveUnlock on unlock intents. This includes the bouncer showing or tapping on 71 * a notification. May also include wakeups: [wakeupsConsideredUnlockIntents]. 72 */ 73 UNLOCK_INTENT, 74 75 /** 76 * Trigger ActiveUnlock on biometric failures. This may include soft errors depending on 77 * the other settings. See: [faceErrorsToTriggerBiometricFailOn], 78 * [faceAcquireInfoToTriggerBiometricFailOn]. 79 */ 80 BIOMETRIC_FAIL, 81 82 /** 83 * Trigger ActiveUnlock when the assistant is triggered. 84 */ 85 ASSISTANT, 86 } 87 88 /** 89 * Biometric type options. 90 */ 91 enum class BiometricType(val intValue: Int) { 92 NONE(0), 93 ANY_FACE(1), 94 ANY_FINGERPRINT(2), 95 UNDER_DISPLAY_FINGERPRINT(3), 96 } 97 98 var keyguardUpdateMonitor: KeyguardUpdateMonitor? = null 99 private var requestActiveUnlockOnWakeup = false 100 private var requestActiveUnlockOnUnlockIntent = false 101 private var requestActiveUnlockOnBioFail = false 102 103 private var faceErrorsToTriggerBiometricFailOn = mutableSetOf<Int>() 104 private var faceAcquireInfoToTriggerBiometricFailOn = mutableSetOf<Int>() 105 private var onUnlockIntentWhenBiometricEnrolled = mutableSetOf<Int>() 106 private var wakeupsConsideredUnlockIntents = mutableSetOf<Int>() 107 private var wakeupsToForceDismissKeyguard = mutableSetOf<Int>() 108 109 private val settingsObserver = object : ContentObserver(handler) { 110 private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE) 111 private val unlockIntentUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT) 112 private val bioFailUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL) 113 private val faceErrorsUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS) 114 private val faceAcquireInfoUri = 115 secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO) 116 private val unlockIntentWhenBiometricEnrolledUri = 117 secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED) 118 private val wakeupsConsideredUnlockIntentsUri = 119 secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS) 120 private val wakeupsToForceDismissKeyguardUri = 121 secureSettings.getUriFor(ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD) 122 123 fun register() { 124 registerUri( 125 listOf( 126 wakeUri, 127 unlockIntentUri, 128 bioFailUri, 129 faceErrorsUri, 130 faceAcquireInfoUri, 131 unlockIntentWhenBiometricEnrolledUri, 132 wakeupsConsideredUnlockIntentsUri, 133 wakeupsToForceDismissKeyguardUri, 134 ) 135 ) 136 137 onChange(true, ArrayList(), 0, getCurrentUser()) 138 } 139 140 private fun registerUri(uris: Collection<Uri>) { 141 for (uri in uris) { 142 contentResolver.registerContentObserver( 143 uri, 144 false, 145 this, 146 UserHandle.USER_ALL) 147 } 148 } 149 150 override fun onChange( 151 selfChange: Boolean, 152 uris: Collection<Uri>, 153 flags: Int, 154 userId: Int 155 ) { 156 if (getCurrentUser() != userId) { 157 return 158 } 159 160 if (selfChange || uris.contains(wakeUri)) { 161 requestActiveUnlockOnWakeup = secureSettings.getIntForUser( 162 ACTIVE_UNLOCK_ON_WAKE, 0, getCurrentUser()) == 1 163 } 164 165 if (selfChange || uris.contains(unlockIntentUri)) { 166 requestActiveUnlockOnUnlockIntent = secureSettings.getIntForUser( 167 ACTIVE_UNLOCK_ON_UNLOCK_INTENT, 0, getCurrentUser()) == 1 168 } 169 170 if (selfChange || uris.contains(bioFailUri)) { 171 requestActiveUnlockOnBioFail = secureSettings.getIntForUser( 172 ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0, getCurrentUser()) == 1 173 } 174 175 if (selfChange || uris.contains(faceErrorsUri)) { 176 processStringArray( 177 secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ERRORS, 178 getCurrentUser()), 179 faceErrorsToTriggerBiometricFailOn, 180 setOf(FACE_ERROR_TIMEOUT)) 181 } 182 183 if (selfChange || uris.contains(faceAcquireInfoUri)) { 184 processStringArray( 185 secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, 186 getCurrentUser()), 187 faceAcquireInfoToTriggerBiometricFailOn, 188 emptySet()) 189 } 190 191 if (selfChange || uris.contains(unlockIntentWhenBiometricEnrolledUri)) { 192 processStringArray( 193 secureSettings.getStringForUser( 194 ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED, 195 getCurrentUser()), 196 onUnlockIntentWhenBiometricEnrolled, 197 setOf(BiometricType.NONE.intValue)) 198 } 199 200 if (selfChange || uris.contains(wakeupsConsideredUnlockIntentsUri)) { 201 processStringArray( 202 secureSettings.getStringForUser( 203 ACTIVE_UNLOCK_WAKEUPS_CONSIDERED_UNLOCK_INTENTS, 204 getCurrentUser()), 205 wakeupsConsideredUnlockIntents, 206 setOf(WAKE_REASON_UNFOLD_DEVICE)) 207 } 208 209 if (selfChange || uris.contains(wakeupsToForceDismissKeyguardUri)) { 210 processStringArray( 211 secureSettings.getStringForUser( 212 ACTIVE_UNLOCK_WAKEUPS_TO_FORCE_DISMISS_KEYGUARD, 213 getCurrentUser()), 214 wakeupsToForceDismissKeyguard, 215 setOf(WAKE_REASON_UNFOLD_DEVICE)) 216 } 217 } 218 219 /** 220 * Convert a pipe-separated set of integers into a set of ints. 221 * @param stringSetting expected input are integers delineated by a pipe. For example, 222 * it may look something like this: "1|5|3". 223 * @param out updates the "out" Set will the integers between the pipes. 224 * @param default If stringSetting is null, "out" will be populated with values in "default" 225 */ 226 private fun processStringArray( 227 stringSetting: String?, 228 out: MutableSet<Int>, 229 default: Set<Int> 230 ) { 231 out.clear() 232 stringSetting?.let { 233 for (code: String in stringSetting.split("|")) { 234 if (code.isNotEmpty()) { 235 try { 236 out.add(code.toInt()) 237 } catch (e: NumberFormatException) { 238 Log.e(TAG, "Passed an invalid setting=$code") 239 } 240 } 241 } 242 } ?: out.addAll(default) 243 } 244 } 245 246 init { 247 settingsObserver.register() 248 dumpManager.registerDumpable(this) 249 } 250 251 /** 252 * If any active unlock triggers are enabled. 253 */ 254 fun isActiveUnlockEnabled(): Boolean { 255 return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent || 256 requestActiveUnlockOnBioFail 257 } 258 259 /** 260 * Whether the face error code from {@link BiometricFaceConstants} should trigger 261 * active unlock on biometric failure. 262 */ 263 fun shouldRequestActiveUnlockOnFaceError(errorCode: Int): Boolean { 264 return faceErrorsToTriggerBiometricFailOn.contains(errorCode) 265 } 266 267 /** 268 * Whether the face acquireInfo from {@link BiometricFaceConstants} should trigger 269 * active unlock on biometric failure. 270 */ 271 fun shouldRequestActiveUnlockOnFaceAcquireInfo(acquiredInfo: Int): Boolean { 272 return faceAcquireInfoToTriggerBiometricFailOn.contains(acquiredInfo) 273 } 274 275 /** 276 * Whether the PowerManager wake reason is considered an unlock intent and should use origin 277 * [ActiveUnlockRequestOrigin.UNLOCK_INTENT] instead of [ActiveUnlockRequestOrigin.WAKE]. 278 */ 279 fun isWakeupConsideredUnlockIntent(pmWakeReason: Int): Boolean { 280 return wakeupsConsideredUnlockIntents.contains(pmWakeReason) 281 } 282 283 /** 284 * Whether the PowerManager wake reason should force dismiss the keyguard if active 285 * unlock is successful. 286 */ 287 fun shouldWakeupForceDismissKeyguard(pmWakeReason: Int): Boolean { 288 return wakeupsToForceDismissKeyguard.contains(pmWakeReason) 289 } 290 291 /** 292 * Whether to trigger active unlock based on where the request is coming from and 293 * the current settings. 294 */ 295 fun shouldAllowActiveUnlockFromOrigin(requestOrigin: ActiveUnlockRequestOrigin): Boolean { 296 return when (requestOrigin) { 297 ActiveUnlockRequestOrigin.WAKE -> requestActiveUnlockOnWakeup 298 299 ActiveUnlockRequestOrigin.UNLOCK_INTENT -> 300 requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup || 301 (shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()) 302 303 ActiveUnlockRequestOrigin.BIOMETRIC_FAIL -> 304 requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntent || 305 requestActiveUnlockOnWakeup 306 307 ActiveUnlockRequestOrigin.ASSISTANT -> isActiveUnlockEnabled() 308 } 309 } 310 311 private fun shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment(): Boolean { 312 if (!requestActiveUnlockOnBioFail) { 313 return false 314 } 315 316 keyguardUpdateMonitor?.let { 317 val anyFaceEnrolled = it.isFaceEnrolled 318 val anyFingerprintEnrolled = 319 it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser()) 320 val udfpsEnrolled = it.isUdfpsEnrolled 321 322 if (!anyFaceEnrolled && !anyFingerprintEnrolled) { 323 return onUnlockIntentWhenBiometricEnrolled.contains(BiometricType.NONE.intValue) 324 } 325 326 if (!anyFaceEnrolled && anyFingerprintEnrolled) { 327 return onUnlockIntentWhenBiometricEnrolled.contains( 328 BiometricType.ANY_FINGERPRINT.intValue) || 329 (udfpsEnrolled && onUnlockIntentWhenBiometricEnrolled.contains( 330 BiometricType.UNDER_DISPLAY_FINGERPRINT.intValue)) 331 } 332 333 if (!anyFingerprintEnrolled && anyFaceEnrolled) { 334 return onUnlockIntentWhenBiometricEnrolled.contains(BiometricType.ANY_FACE.intValue) 335 } 336 } 337 338 return false 339 } 340 341 override fun dump(pw: PrintWriter, args: Array<out String>) { 342 pw.println("Settings:") 343 pw.println(" requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup") 344 pw.println(" requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent") 345 pw.println(" requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail") 346 347 val onUnlockIntentWhenBiometricEnrolledString = 348 onUnlockIntentWhenBiometricEnrolled.map { 349 for (biometricType in BiometricType.values()) { 350 if (biometricType.intValue == it) { 351 return@map biometricType.name 352 } 353 } 354 return@map "UNKNOWN" 355 } 356 pw.println(" requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=" + 357 "$onUnlockIntentWhenBiometricEnrolledString") 358 pw.println(" requestActiveUnlockOnFaceError=$faceErrorsToTriggerBiometricFailOn") 359 pw.println(" requestActiveUnlockOnFaceAcquireInfo=" + 360 "$faceAcquireInfoToTriggerBiometricFailOn") 361 pw.println(" activeUnlockWakeupsConsideredUnlockIntents=${ 362 wakeupsConsideredUnlockIntents.map { PowerManager.wakeReasonToString(it) } 363 }") 364 pw.println(" activeUnlockFromWakeupsToAlwaysDismissKeyguard=${ 365 wakeupsToForceDismissKeyguard.map { PowerManager.wakeReasonToString(it) } 366 }") 367 368 pw.println("Current state:") 369 keyguardUpdateMonitor?.let { 370 pw.println(" shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" + 371 "${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}") 372 pw.println(" faceEnrolled=${it.isFaceEnrolled}") 373 pw.println(" fpEnrolled=${ 374 it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())}") 375 pw.println(" udfpsEnrolled=${it.isUdfpsEnrolled}") 376 } ?: pw.println(" keyguardUpdateMonitor is uninitialized") 377 } 378 }