Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 31354318 authored by Beverly's avatar Beverly
Browse files

Log biometric lockout events

Test: manually checked events using go/aster-event-viewing
Test: atest KeyguardBiometricLockoutLoggerTest
Fixes: 205762820
Bug: 213483562
Bug: 213484709
Change-Id: Id639e5bdc834ee72fdea4877b73b3e8c63170bfb
parent 4b10475f
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -294,6 +294,7 @@
    <string-array name="config_systemUIServiceComponents" translatable="false">
        <item>com.android.systemui.util.NotificationChannels</item>
        <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
        <item>com.android.keyguard.KeyguardBiometricLockoutLogger</item>
        <item>com.android.systemui.recents.Recents</item>
        <item>com.android.systemui.volume.VolumeUI</item>
        <item>com.android.systemui.statusbar.phone.StatusBar</item>
+176 −0
Original line number Diff line number Diff line
/*
 * 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.content.Context
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.dump.DumpManager
import java.io.FileDescriptor
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(
    context: Context?,
    private val uiEventLogger: UiEventLogger,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val dumpManager: DumpManager
) : CoreStartable(context) {
    private var fingerprintLockedOut = false
    private var faceLockedOut = false
    private var encryptedOrLockdown = false
    private var unattendedUpdate = false
    private var timeout = false

    override fun start() {
        dumpManager.registerDumpable(this)
        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) {
                    uiEventLogger.log(
                            PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT)
                } else if (!lockedOut && fingerprintLockedOut) {
                    uiEventLogger.log(
                            PrimaryAuthRequiredEvent
                                    .PRIMARY_AUTH_REQUIRED_FINGERPRINT_LOCKED_OUT_RESET)
                }
                fingerprintLockedOut = lockedOut
            } else if (biometricSourceType == BiometricSourceType.FACE) {
                val lockedOut = keyguardUpdateMonitor.isFaceLockedOut
                if (lockedOut && !faceLockedOut) {
                    uiEventLogger.log(
                            PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_FACE_LOCKED_OUT)
                } else if (!lockedOut && faceLockedOut) {
                    uiEventLogger.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) {
                uiEventLogger.log(
                        PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_ENCRYPTED_OR_LOCKDOWN)
            }
            encryptedOrLockdown = newEncryptedOrLockdown

            val newUnattendedUpdate = isUnattendedUpdate(strongAuthFlags)
            if (newUnattendedUpdate && !unattendedUpdate) {
                uiEventLogger.log(PrimaryAuthRequiredEvent.PRIMARY_AUTH_REQUIRED_UNATTENDED_UPDATE)
            }
            unattendedUpdate = newUnattendedUpdate

            val newTimeout = isStrongAuthTimeout(strongAuthFlags)
            if (newTimeout && !timeout) {
                uiEventLogger.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)

    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
        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
        }
    }
}
 No newline at end of file
+9 −4
Original line number Diff line number Diff line
@@ -183,7 +183,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
    private static final int MSG_USER_STOPPED = 340;
    private static final int MSG_USER_REMOVED = 341;
    private static final int MSG_KEYGUARD_GOING_AWAY = 342;
    private static final int MSG_LOCK_SCREEN_MODE = 343;
    private static final int MSG_TIME_FORMAT_UPDATE = 344;
    private static final int MSG_REQUIRE_NFC_UNLOCK = 345;

@@ -221,7 +220,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
    private static final int BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED = -1;
    public static final int BIOMETRIC_HELP_FACE_NOT_RECOGNIZED = -2;

    private static final int DEFAULT_CHARGING_VOLTAGE_MICRO_VOLT = 5000000;
    /**
     * If no cancel signal has been received after this amount of time, set the biometric running
     * state to stopped to allow Keyguard to retry authentication.
@@ -231,7 +229,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
    private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
            "com.android.settings", "com.android.settings.FallbackHome");


    /**
     * If true, the system is in the half-boot-to-decryption-screen state.
     * Prudently disable lockscreen.
@@ -1250,7 +1247,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
    }

    private boolean isEncryptedOrLockdown(int userId) {
    /**
     * Returns true if primary authentication is required for the given user due to lockdown
     * or encryption after reboot.
     */
    public boolean isEncryptedOrLockdown(int userId) {
        final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(userId);
        final boolean isLockDown =
                containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
@@ -2514,6 +2515,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
        return mFingerprintLockedOut || mFingerprintLockedOutPermanent;
    }

    public boolean isFaceLockedOut() {
        return mFaceLockedOutPermanent;
    }

    /**
     * If biometrics hardware is available, not disabled, and user has enrolled templates.
     * This does NOT check if the device is encrypted or in lockdown.
+8 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.dagger;

import com.android.keyguard.KeyguardBiometricLockoutLogger;
import com.android.systemui.CoreStartable;
import com.android.systemui.LatencyTester;
import com.android.systemui.ScreenDecorations;
@@ -90,6 +91,13 @@ public abstract class SystemUIBinder {
    @ClassKey(KeyguardViewMediator.class)
    public abstract CoreStartable bindKeyguardViewMediator(KeyguardViewMediator sysui);

    /** Inject into KeyguardBiometricLockoutLogger. */
    @Binds
    @IntoMap
    @ClassKey(KeyguardBiometricLockoutLogger.class)
    public abstract CoreStartable bindKeyguardBiometricLockoutLogger(
            KeyguardBiometricLockoutLogger sysui);

    /** Inject into LatencyTests. */
    @Binds
    @IntoMap
+1 −1
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@
 * 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
 * limitations under the License.
 */

package com.android.systemui.statusbar.phone;
Loading