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

Commit cd5ef0a3 authored by Beverly's avatar Beverly
Browse files

Don't add the UdfpsView until after the device is finishedGoingToSleep

Bug: 225183106
Flag: ACONFIG com.android.systemui.udfps_view_performance DEVELOPMENT
Test: atest UdfpsControllerTest
Test: atest UdfpsControllerOverlayTest
Change-Id: Ib29d8a57d4a27eeb7ba30078d4f8f67538067af9
parent dc5a7c2b
Loading
Loading
Loading
Loading
+85 −0
Original line number Diff line number Diff line
@@ -44,12 +44,18 @@ import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayView
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.FakePowerRepository
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
import com.android.systemui.statusbar.phone.SystemUIDialogManager
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
@@ -58,6 +64,10 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -68,6 +78,7 @@ import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
@@ -120,6 +131,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
    @Mock private lateinit var shadeInteractor: ShadeInteractor
    @Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
    @Mock private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
    private lateinit var powerRepository: FakePowerRepository
    private lateinit var powerInteractor: PowerInteractor
    private lateinit var testScope: TestScope

    private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
    private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
@@ -127,6 +141,15 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {

    @Before
    fun setup() {
        testScope = TestScope(StandardTestDispatcher())
        powerRepository = FakePowerRepository()
        powerInteractor =
            PowerInteractor(
                powerRepository,
                mock(FalsingCollector::class.java),
                mock(ScreenOffAnimationController::class.java),
                statusBarStateController,
            )
        whenever(inflater.inflate(R.layout.udfps_view, null, false)).thenReturn(udfpsView)
        whenever(inflater.inflate(R.layout.udfps_bp_view, null))
            .thenReturn(mock(UdfpsBpView::class.java))
@@ -178,6 +201,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
                { defaultUdfpsTouchOverlayViewModel },
                shadeInteractor,
                udfpsOverlayInteractor,
                powerInteractor,
                testScope,
            )
        block()
    }
@@ -277,6 +302,66 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
            }
        }

    @Test
    fun showUdfpsOverlay_awake() =
        testScope.runTest {
            withReason(REASON_AUTH_KEYGUARD) {
                mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
                powerRepository.updateWakefulness(
                    rawState = WakefulnessState.AWAKE,
                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
                    lastSleepReason = WakeSleepReason.OTHER,
                )
                controllerOverlay.show(udfpsController, overlayParams)
                runCurrent()
                verify(windowManager).addView(any(), any())
            }
        }

    @Test
    fun showUdfpsOverlay_whileGoingToSleep() =
        testScope.runTest {
            withReason(REASON_AUTH_KEYGUARD) {
                mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
                powerRepository.updateWakefulness(
                    rawState = WakefulnessState.STARTING_TO_SLEEP,
                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
                    lastSleepReason = WakeSleepReason.OTHER,
                )
                controllerOverlay.show(udfpsController, overlayParams)
                runCurrent()
                verify(windowManager, never()).addView(any(), any())

                // we hide to end the job that listens for the finishedGoingToSleep signal
                controllerOverlay.hide()
            }
        }

    @Test
    fun showUdfpsOverlay_afterFinishedGoingToSleep() =
        testScope.runTest {
            withReason(REASON_AUTH_KEYGUARD) {
                mSetFlagsRule.enableFlags(Flags.FLAG_UDFPS_VIEW_PERFORMANCE)
                powerRepository.updateWakefulness(
                    rawState = WakefulnessState.STARTING_TO_SLEEP,
                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
                    lastSleepReason = WakeSleepReason.OTHER,
                )
                controllerOverlay.show(udfpsController, overlayParams)
                runCurrent()
                verify(windowManager, never()).addView(any(), any())

                powerRepository.updateWakefulness(
                    rawState = WakefulnessState.ASLEEP,
                    lastWakeReason = WakeSleepReason.POWER_BUTTON,
                    lastSleepReason = WakeSleepReason.OTHER,
                )
                runCurrent()
                verify(windowManager)
                    .addView(eq(controllerOverlay.getTouchOverlay()), layoutParamsCaptor.capture())
            }
        }

    private fun showUdfpsOverlay() {
        val didShow = controllerOverlay.show(udfpsController, overlayParams)

+26 −1
Original line number Diff line number Diff line
@@ -86,6 +86,7 @@ import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayView
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
@@ -94,10 +95,15 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.data.repository.FakePowerRepository;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.power.shared.model.WakeSleepReason;
import com.android.systemui.power.shared.model.WakefulnessState;
import com.android.systemui.res.R;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
@@ -125,6 +131,8 @@ import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.List;

import kotlinx.coroutines.CoroutineScope;

@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
@@ -238,6 +246,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
    private ScreenLifecycle.Observer mScreenObserver;
    private FingerprintSensorPropertiesInternal mOpticalProps;
    private FingerprintSensorPropertiesInternal mUltrasonicProps;
    private PowerInteractor mPowerInteractor;
    private FakePowerRepository mPowerRepository;
    @Mock
    private InputManager mInputManager;
    @Mock
@@ -253,6 +263,19 @@ public class UdfpsControllerTest extends SysuiTestCase {

    @Before
    public void setUp() {
        mPowerRepository = new FakePowerRepository();
        mPowerInteractor = new PowerInteractor(
                mPowerRepository,
                mock(FalsingCollector.class),
                mock(ScreenOffAnimationController.class),
                mStatusBarStateController
        );
        mPowerRepository.updateWakefulness(
                WakefulnessState.AWAKE,
                WakeSleepReason.POWER_BUTTON,
                WakeSleepReason.OTHER,
                /* powerButtonLaunchGestureTriggered */ false
        );
        mContext.getOrCreateTestableResources()
                .addOverride(com.android.internal.R.bool.config_ignoreUdfpsVote, false);

@@ -346,7 +369,9 @@ public class UdfpsControllerTest extends SysuiTestCase {
                mKeyguardTransitionInteractor,
                mDeviceEntryUdfpsTouchOverlayViewModel,
                mDefaultUdfpsTouchOverlayViewModel,
                mUdfpsOverlayInteractor
                mUdfpsOverlayInteractor,
                mPowerInteractor,
                mock(CoroutineScope.class)
        );
        verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
        mOverlayController = mOverlayCaptor.getValue();
+13 −2
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlay
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
@@ -90,6 +91,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.shade.domain.interactor.ShadeInteractor;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -116,6 +118,7 @@ import java.util.concurrent.Executor;

import javax.inject.Inject;

import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.ExperimentalCoroutinesApi;

/**
@@ -173,6 +176,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
            mDefaultUdfpsTouchOverlayViewModel;
    @NonNull private final AlternateBouncerInteractor mAlternateBouncerInteractor;
    @NonNull private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
    @NonNull private final PowerInteractor mPowerInteractor;
    @NonNull private final CoroutineScope mScope;
    @NonNull private final InputManager mInputManager;
    @NonNull private final UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
    @NonNull private final SelectedUserInteractor mSelectedUserInteractor;
@@ -296,7 +301,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {
                        mDeviceEntryUdfpsTouchOverlayViewModel,
                        mDefaultUdfpsTouchOverlayViewModel,
                        mShadeInteractor,
                        mUdfpsOverlayInteractor
                        mUdfpsOverlayInteractor,
                        mPowerInteractor,
                        mScope
                    )));
        }

@@ -678,7 +685,9 @@ public class UdfpsController implements DozeReceiver, Dumpable {
            @NonNull KeyguardTransitionInteractor keyguardTransitionInteractor,
            Lazy<DeviceEntryUdfpsTouchOverlayViewModel> deviceEntryUdfpsTouchOverlayViewModel,
            Lazy<DefaultUdfpsTouchOverlayViewModel> defaultUdfpsTouchOverlayViewModel,
            @NonNull UdfpsOverlayInteractor udfpsOverlayInteractor) {
            @NonNull UdfpsOverlayInteractor udfpsOverlayInteractor,
            @NonNull PowerInteractor powerInteractor,
            @Application CoroutineScope scope) {
        mContext = context;
        mExecution = execution;
        mVibrator = vibrator;
@@ -720,6 +729,8 @@ public class UdfpsController implements DozeReceiver, Dumpable {
        mShadeInteractor = shadeInteractor;
        mAlternateBouncerInteractor = alternateBouncerInteractor;
        mUdfpsOverlayInteractor = udfpsOverlayInteractor;
        mPowerInteractor = powerInteractor;
        mScope = scope;
        mInputManager = inputManager;
        mUdfpsKeyguardAccessibilityDelegate = udfpsKeyguardAccessibilityDelegate;
        mSelectedUserInteractor = selectedUserInteractor;
+89 −32
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan
import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.Flags.udfpsViewPerformance
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
@@ -53,10 +54,13 @@ import com.android.systemui.biometrics.ui.viewmodel.DefaultUdfpsTouchOverlayView
import com.android.systemui.biometrics.ui.viewmodel.DeviceEntryUdfpsTouchOverlayViewModel
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -67,7 +71,13 @@ import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import dagger.Lazy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

private const val TAG = "UdfpsControllerOverlay"

@@ -107,11 +117,20 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
        private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
        private val transitionInteractor: KeyguardTransitionInteractor,
        private val selectedUserInteractor: SelectedUserInteractor,
    private val deviceEntryUdfpsTouchOverlayViewModel: Lazy<DeviceEntryUdfpsTouchOverlayViewModel>,
        private val deviceEntryUdfpsTouchOverlayViewModel:
            Lazy<DeviceEntryUdfpsTouchOverlayViewModel>,
        private val defaultUdfpsTouchOverlayViewModel: Lazy<DefaultUdfpsTouchOverlayViewModel>,
        private val shadeInteractor: ShadeInteractor,
        private val udfpsOverlayInteractor: UdfpsOverlayInteractor,
        private val powerInteractor: PowerInteractor,
        @Application private val scope: CoroutineScope,
) {
    private val isFinishedGoingToSleep: Flow<Unit> =
        powerInteractor.detailedWakefulness
            .filter { it.internalWakefulnessState == WakefulnessState.ASLEEP }
            .map { } // map to Unit
    private var listenForAsleepJob: Job? = null
    private var addViewRunnable: Runnable? = null
    private var overlayViewLegacy: UdfpsView? = null
        private set
    private var overlayTouchView: UdfpsTouchOverlay? = null
@@ -192,7 +211,8 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
                        if (requestReason.isImportantForAccessibility()) {
                            importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
                        }
                        windowManager.addView(this, coreLayoutParams.updateDimensions(null))

                        addViewNowOrLater(this, null)
                        when (requestReason) {
                            REASON_AUTH_KEYGUARD ->
                                UdfpsTouchOverlayBinder.bind(
@@ -225,7 +245,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
                            importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
                        }

                        windowManager.addView(this, coreLayoutParams.updateDimensions(animation))
                        addViewNowOrLater(this, animation)
                        sensorRect = sensorBounds
                    }
                }
@@ -257,6 +277,41 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
        return false
    }

    private fun addViewNowOrLater(view: View, animation: UdfpsAnimationViewController<*>?) {
        if (udfpsViewPerformance()) {
            addViewRunnable = kotlinx.coroutines.Runnable {
                windowManager.addView(
                        view,
                        coreLayoutParams.updateDimensions(animation)
                )
            }
            if (powerInteractor.detailedWakefulness.value.internalWakefulnessState
                    != WakefulnessState.STARTING_TO_SLEEP) {
                addViewIfPending()
            } else {
                listenForAsleepJob?.cancel()
                listenForAsleepJob = scope.launch {
                    isFinishedGoingToSleep.collect {
                        addViewIfPending()
                    }
                }
            }
        } else {
            windowManager.addView(
                    view,
                    coreLayoutParams.updateDimensions(animation)
            )
        }
    }

    private fun addViewIfPending() {
        addViewRunnable?.let {
            listenForAsleepJob?.cancel()
            it.run()
        }
        addViewRunnable = null
    }

    fun inflateUdfpsAnimation(
        view: UdfpsView,
        controller: UdfpsController
@@ -368,6 +423,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
        overlayViewLegacy = null
        overlayTouchView = null
        overlayTouchListener = null
        listenForAsleepJob?.cancel()

        return wasShowing
    }
@@ -412,7 +468,8 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
        if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
            if (!shouldRotate(animation)) {
                Log.v(
                    TAG, "Skip rotating UDFPS bounds " + Surface.rotationToString(rot) +
                    TAG,
                    "Skip rotating UDFPS bounds " + Surface.rotationToString(rot) +
                        " animation=$animation" +
                        " isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
                        " isOccluded=${keyguardStateController.isOccluded}"