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

Commit 478575df authored by Wu Ahan's avatar Wu Ahan
Browse files

Instrument the latency of fps unlock to home

This change includes both UDFPS and SFPS instrument.

Flag: None
Bug: 297856667
Test: Check logs and traces in b/297856667#comment3
Test: atest SystemUITests
Test: abtd with platinum, update result in the bug
Change-Id: I293b8692cb18dc2951c73dc0ec4eded1658841cf
parent a8191f8d
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPOR
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED;
@@ -222,6 +223,11 @@ public class LatencyTracker {
     */
    public static final int ACTION_NOTIFICATION_BIG_PICTURE_LOADED = 23;

    /**
     * Time it takes to unlock the device via udfps, until the whole launcher appears.
     */
    public static final int ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME = 24;

    private static final int[] ACTIONS_ALL = {
        ACTION_EXPAND_PANEL,
        ACTION_TOGGLE_RECENTS,
@@ -247,6 +253,7 @@ public class LatencyTracker {
        ACTION_REQUEST_IME_HIDDEN,
        ACTION_SMARTSPACE_DOORBELL,
        ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
        ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
    };

    /** @hide */
@@ -275,6 +282,7 @@ public class LatencyTracker {
        ACTION_REQUEST_IME_HIDDEN,
        ACTION_SMARTSPACE_DOORBELL,
        ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
        ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface Action {
@@ -306,6 +314,7 @@ public class LatencyTracker {
            UIACTION_LATENCY_REPORTED__ACTION__ACTION_REQUEST_IME_HIDDEN,
            UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL,
            UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
            UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
    };

    private final Object mLock = new Object();
@@ -492,6 +501,8 @@ public class LatencyTracker {
                return "ACTION_SMARTSPACE_DOORBELL";
            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED:
                return "ACTION_NOTIFICATION_BIG_PICTURE_LOADED";
            case UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME:
                return "ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME";
            default:
                throw new IllegalArgumentException("Invalid action");
        }
+208 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.systemui.biometrics

import android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START
import android.hardware.biometrics.BiometricSourceType
import android.hardware.biometrics.BiometricSourceType.FINGERPRINT
import android.util.Log
import com.android.app.tracing.TraceStateLogger
import com.android.internal.util.LatencyTracker
import com.android.internal.util.LatencyTracker.ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener
import com.android.systemui.plugins.statusbar.StatusBarStateController
import javax.inject.Inject

private const val TAG = "FpsUnlockTracker"
private const val TRACE_COUNTER_NAME = "FpsUnlockStage"
private const val TRACE_TAG_AOD = "AOD"
private const val TRACE_TAG_KEYGUARD = "KEYGUARD"
private const val DEBUG = true

/** This is a class for monitoring unlock latency of fps and logging stages in perfetto. */
@SysUISingleton
class FpsUnlockTracker
@Inject
constructor(
    private val statusBarStateController: StatusBarStateController,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
    private val latencyTracker: LatencyTracker,
) {
    private val fpsTraceStateLogger = TraceStateLogger(TRACE_COUNTER_NAME)
    private var fpsAuthenticated: Boolean = false

    private val keyguardUpdateMonitorCallback =
        object : KeyguardUpdateMonitorCallback() {
            override fun onBiometricAcquired(
                biometricSourceType: BiometricSourceType?,
                acquireInfo: Int
            ) {
                if (keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed) {
                    onHalAuthenticationStage(acquireInfo)
                }
            }

            override fun onBiometricAuthenticated(
                userId: Int,
                biometricSourceType: BiometricSourceType?,
                isStrongBiometric: Boolean
            ) {
                if (biometricSourceType == FINGERPRINT) {
                    fpsAuthenticated = true
                    onExitKeyguard()
                }
            }

            override fun onBiometricError(
                msgId: Int,
                errString: String?,
                biometricSourceType: BiometricSourceType?
            ) {
                if (biometricSourceType == FINGERPRINT) {
                    latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
                }
            }

            override fun onBiometricRunningStateChanged(
                running: Boolean,
                biometricSourceType: BiometricSourceType?
            ) {
                if (biometricSourceType != FINGERPRINT || !running) {
                    return
                }
                onWaitForAuthenticationStage()
            }
        }

    private val keyguardUnlockAnimationListener =
        object : KeyguardUnlockAnimationListener {
            override fun onUnlockAnimationFinished() = onUnlockedStage()
        }

    /** Start tracking the fps unlock. */
    fun startTracking() {
        keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
        keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener(
            keyguardUnlockAnimationListener
        )
    }

    /** Stop tracking the fps unlock. */
    fun stopTracking() {
        keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
        keyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener(
            keyguardUnlockAnimationListener
        )
    }

    /**
     * The stage when the devices is locked and is possible to be unlocked via fps. However, in some
     * situations, it might be unlocked only via bouncer.
     */
    fun onWaitForAuthenticationStage() {
        val stage =
            if (keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed)
                FpsUnlockStage.WAIT_FOR_AUTHENTICATION.name
            else FpsUnlockStage.WAIT_FOR_AUTHENTICATION.name + "(Not allowed)"
        fpsTraceStateLogger.log(stage)
        if (DEBUG) {
            Log.d(TAG, "onWaitForAuthenticationStage: stage=$stage")
        }
    }

    /**
     * The stage dedicated to UDFPS, SFPS should not enter this stage. The only place where invokes
     * this function is UdfpsController#onFingerDown.
     */
    fun onUiReadyStage() {
        if (!keyguardUpdateMonitor.isUdfpsSupported || !keyguardUpdateMonitor.isUdfpsEnrolled) {
            return
        }
        fpsTraceStateLogger.log(FpsUnlockStage.UI_READY.name)
        startLatencyTracker()
        if (DEBUG) {
            Log.d(TAG, "onUiReadyStage: dozing=${statusBarStateController.isDozing}")
        }
    }

    /** The stage when the HAL is authenticating the fingerprint. */
    fun onHalAuthenticationStage(acquire: Int) {
        fpsTraceStateLogger.log("${FpsUnlockStage.HAL_AUTHENTICATION.name}($acquire)")
        // Start latency tracker here only for SFPS, UDFPS should start at onUiReadyStage.
        if (
            keyguardUpdateMonitor.isSfpsSupported &&
                keyguardUpdateMonitor.isSfpsEnrolled &&
                acquire == FINGERPRINT_ACQUIRED_START
        ) {
            startLatencyTracker()
        }
        if (DEBUG) {
            Log.d(
                TAG,
                "onHalAuthenticationStage: acquire=$acquire" +
                    ", sfpsSupported=${keyguardUpdateMonitor.isSfpsSupported}" +
                    ", sfpsEnrolled=${keyguardUpdateMonitor.isSfpsEnrolled}"
            )
        }
    }

    /** The stage when the authentication is succeeded and is going to exit keyguard. */
    fun onExitKeyguard() {
        fpsTraceStateLogger.log(FpsUnlockStage.EXIT_KEYGUARD.name)
        if (DEBUG) {
            Log.d(TAG, "onExitKeyguard: fpsAuthenticated=$fpsAuthenticated")
        }
    }

    /**
     * The stage when the unlock animation is finished which means the user can start interacting
     * with the device.
     */
    fun onUnlockedStage() {
        fpsTraceStateLogger.log(FpsUnlockStage.UNLOCKED.name)
        if (fpsAuthenticated) {
            // The device is unlocked successfully via fps, end the instrument.
            latencyTracker.onActionEnd(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
        } else {
            // The device is unlocked but not via fps, maybe bouncer? Cancel the instrument.
            latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
        }
        if (DEBUG) {
            Log.d(TAG, "onUnlockedStage: fpsAuthenticated=$fpsAuthenticated")
        }
        fpsAuthenticated = false
    }

    private fun startLatencyTracker() {
        latencyTracker.onActionCancel(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME)
        val tag = if (statusBarStateController.isDozing) TRACE_TAG_AOD else TRACE_TAG_KEYGUARD
        latencyTracker.onActionStart(ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME, tag)
    }
}

private enum class FpsUnlockStage {
    WAIT_FOR_AUTHENTICATION,
    UI_READY,
    HAL_AUTHENTICATION,
    EXIT_KEYGUARD,
    UNLOCKED
}
+3 −1
Original line number Diff line number Diff line
@@ -91,7 +91,8 @@ constructor(
    @Main private val handler: Handler,
    private val alternateBouncerInteractor: AlternateBouncerInteractor,
    @Application private val scope: CoroutineScope,
    dumpManager: DumpManager
    dumpManager: DumpManager,
    fpsUnlockTracker: FpsUnlockTracker
) : Dumpable {
    private val requests: HashSet<SideFpsUiRequestSource> = HashSet()

@@ -167,6 +168,7 @@ constructor(
            }

    init {
        fpsUnlockTracker.startTracking()
        fingerprintManager?.setSidefpsController(
            object : ISidefpsController.Stub() {
                override fun show(
+10 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_
import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING;
import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;

import static com.android.internal.util.LatencyTracker.ACTION_UDFPS_ILLUMINATE;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;

@@ -167,6 +168,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
    @NonNull private final InputManager mInputManager;
    @NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
    @NonNull private final SelectedUserInteractor mSelectedUserInteractor;
    @NonNull private final FpsUnlockTracker mFpsUnlockTracker;
    private final boolean mIgnoreRefreshRate;

    // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
@@ -646,7 +648,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
            @NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
            @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
            @NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider,
            @NonNull SelectedUserInteractor selectedUserInteractor) {
            @NonNull SelectedUserInteractor selectedUserInteractor,
            @NonNull FpsUnlockTracker fpsUnlockTracker) {
        mContext = context;
        mExecution = execution;
        mVibrator = vibrator;
@@ -690,6 +693,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
        mInputManager = inputManager;
        mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
        mSelectedUserInteractor = selectedUserInteractor;
        mFpsUnlockTracker = fpsUnlockTracker;
        mFpsUnlockTracker.startTracking();

        mTouchProcessor = singlePointerTouchProcessor;
        mSessionTracker = sessionTracker;
@@ -974,7 +979,10 @@ public class UdfpsController implements DozeReceiver, Dumpable {
            return;
        }
        if (isOptical()) {
            mLatencyTracker.onActionStart(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
            mLatencyTracker.onActionStart(ACTION_UDFPS_ILLUMINATE);
        }
        if (getBiometricSessionType() == SESSION_KEYGUARD) {
            mFpsUnlockTracker.onUiReadyStage();
        }
        // Refresh screen timeout and boost process priority if possible.
        mPowerManager.userActivity(mSystemClock.uptimeMillis(),
+4 −2
Original line number Diff line number Diff line
@@ -52,7 +52,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
@@ -64,6 +63,7 @@ import com.android.systemui.display.data.repository.FakeDisplayRepository
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -111,6 +111,7 @@ class SideFpsControllerTest : SysuiTestCase() {
    @Mock lateinit var displayManager: DisplayManager
    @Mock lateinit var handler: Handler
    @Mock lateinit var dumpManager: DumpManager
    @Mock lateinit var fpsUnlockTracker: FpsUnlockTracker
    @Captor lateinit var overlayCaptor: ArgumentCaptor<View>
    @Captor lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>

@@ -269,7 +270,8 @@ class SideFpsControllerTest : SysuiTestCase() {
                handler,
                alternateBouncerInteractor,
                TestCoroutineScope(),
                dumpManager
                dumpManager,
                fpsUnlockTracker
            )
        displayStateRepository.setIsInRearDisplayMode(inRearDisplayMode)

Loading