/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.keyguard import android.app.StatusBarManager.SESSION_KEYGUARD import android.hardware.biometrics.BiometricSourceType import com.android.internal.annotations.VisibleForTesting import com.android.internal.logging.UiEvent import com.android.internal.logging.UiEventLogger import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE import com.android.keyguard.KeyguardBiometricLockoutLogger.PrimaryAuthRequiredEvent import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.log.SessionTracker import java.io.PrintWriter import javax.inject.Inject /** * Logs events when primary authentication requirements change. Primary authentication is considered * authentication using pin/pattern/password input. * * See [PrimaryAuthRequiredEvent] for all the events and their descriptions. */ @SysUISingleton class KeyguardBiometricLockoutLogger @Inject constructor( private val uiEventLogger: UiEventLogger, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val sessionTracker: SessionTracker ) : CoreStartable { private var fingerprintLockedOut = false private var faceLockedOut = false private var encryptedOrLockdown = false private var unattendedUpdate = false private var timeout = false override fun start() { mKeyguardUpdateMonitorCallback.onStrongAuthStateChanged( KeyguardUpdateMonitor.getCurrentUser()) keyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback) } private val mKeyguardUpdateMonitorCallback: KeyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { override fun onLockedOutStateChanged(biometricSourceType: BiometricSourceType) { if (biometricSourceType == BiometricSourceType.FINGERPRINT) { val lockedOut = keyguardUpdateMonitor.isFingerprintLockedOut if (lockedOut && !fingerprintLockedOut) { log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT) } else if (!lockedOut && fingerprintLockedOut) { log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET) } fingerprintLockedOut = lockedOut } else if (biometricSourceType == BiometricSourceType.FACE) { val lockedOut = keyguardUpdateMonitor.isFaceLockedOut if (lockedOut && !faceLockedOut) { log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT) } else if (!lockedOut && faceLockedOut) { log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET) } faceLockedOut = lockedOut } } override fun onStrongAuthStateChanged(userId: Int) { if (userId != KeyguardUpdateMonitor.getCurrentUser()) { return } val strongAuthFlags = keyguardUpdateMonitor.strongAuthTracker .getStrongAuthForUser(userId) val newEncryptedOrLockdown = keyguardUpdateMonitor.isEncryptedOrLockdown(userId) if (newEncryptedOrLockdown && !encryptedOrLockdown) { log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN) } encryptedOrLockdown = newEncryptedOrLockdown val newUnattendedUpdate = isUnattendedUpdate(strongAuthFlags) if (newUnattendedUpdate && !unattendedUpdate) { log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE) } unattendedUpdate = newUnattendedUpdate val newTimeout = isStrongAuthTimeout(strongAuthFlags) if (newTimeout && !timeout) { log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_TIMEOUT) } timeout = newTimeout } } private fun isUnattendedUpdate( @LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int ) = containsFlag(flags, STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) private fun isStrongAuthTimeout( @LockPatternUtils.StrongAuthTracker.StrongAuthFlags flags: Int ) = containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT) || containsFlag(flags, STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT) private fun log(event: PrimaryAuthRequiredEvent) = uiEventLogger.log(event, sessionTracker.getSessionId(SESSION_KEYGUARD)) override fun dump(pw: PrintWriter, args: Array) { pw.println(" mFingerprintLockedOut=$fingerprintLockedOut") pw.println(" mFaceLockedOut=$faceLockedOut") pw.println(" mIsEncryptedOrLockdown=$encryptedOrLockdown") pw.println(" mIsUnattendedUpdate=$unattendedUpdate") pw.println(" mIsTimeout=$timeout") } /** * Events pertaining to whether primary authentication (pin/pattern/password input) is required * for device entry. */ @VisibleForTesting enum class PrimaryAuthRequiredEvent(private val mId: Int) : UiEventLogger.UiEventEnum { @UiEvent(doc = "Fingerprint cannot be used to authenticate for device entry. This" + "can persist until the next primary auth or may timeout.") PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT(924), @UiEvent(doc = "Fingerprint can be used to authenticate for device entry.") PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET(925), @UiEvent(doc = "Face cannot be used to authenticate for device entry.") PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT(926), @UiEvent(doc = "Face can be used to authenticate for device entry.") PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT_RESET(927), @UiEvent(doc = "Device is encrypted (ie: after reboot) or device is locked down by DPM " + "or a manual user lockdown.") PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN(928), @UiEvent(doc = "Primary authentication is required because it hasn't been used for a " + "time required by a device admin or because primary auth hasn't been used for a " + "time after a non-strong biometric (weak or convenience) is used to unlock the " + "device.") PRIMARY_AUTH_REQUIRED_TIMEOUT(929), @UiEvent(doc = "Strong authentication is required to prepare for unattended upgrade.") PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE(931); override fun getId(): Int { return mId } } companion object { private fun containsFlag(strongAuthFlags: Int, flagCheck: Int): Boolean { return strongAuthFlags and flagCheck != 0 } } }