diff options
6 files changed, 240 insertions, 6 deletions
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index b462c21d4da5..87324dba64de 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -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"); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt new file mode 100644 index 000000000000..cf699a2ed349 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FpsUnlockTracker.kt @@ -0,0 +1,208 @@ +/* + * 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 +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index 10e7227fa949..8f61dbfbdd10 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -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( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 3944ac239778..417593787ee9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -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(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index ef06e0efa01d..b4b02a2dfb93 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -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) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index cbde78bb31f7..675ca639493e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -235,6 +235,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private InputManager mInputManager; @Mock private ViewRootImpl mViewRootImpl; + @Mock + private FpsUnlockTracker mFpsUnlockTracker; @Before public void setUp() { @@ -326,7 +328,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mock(KeyguardFaceAuthInteractor.class), mUdfpsKeyguardAccessibilityDelegate, mUdfpsKeyguardViewModels, - mSelectedUserInteractor + mSelectedUserInteractor, + mFpsUnlockTracker ); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); |