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

Commit e96703ba authored by Bill Lin's avatar Bill Lin Committed by Android (Google) Code Review
Browse files

Merge "Fix flicker while usudfps screen off unlock - short term version" into main

parents 8cc936a3 c1837303
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -16,3 +16,14 @@ flag {
    description: "Plugin and related API hooks for contextual auth plugins"
    bug: "373600589"
}

flag {
    name: "udfps_screen_off_unlock_flicker"
    namespace: "biometrics_framework"
    description: "Controls the fix of flicker of udfps screen-off unlock"
    bug: "384986206"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}
+117 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -28,7 +29,10 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.graphics.Point;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.PowerManager;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;

@@ -36,14 +40,21 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.shared.model.FingerprintSensorType;
import com.android.systemui.biometrics.shared.model.SensorStrength;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.flags.DisableSceneContainer;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.keyguard.domain.interactor.DozeInteractor;
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -51,9 +62,9 @@ import com.android.systemui.statusbar.PulseExpansionHandler;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;

import org.junit.Before;
import org.junit.Test;
@@ -64,6 +75,7 @@ import org.mockito.MockitoAnnotations;

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;

@SmallTest
@@ -96,17 +108,23 @@ public class DozeServiceHostTest extends SysuiTestCase {
    @Mock private AuthController mAuthController;
    @Mock private DozeHost.Callback mCallback;
    @Mock private DozeInteractor mDozeInteractor;
    @Mock private AmbientDisplayConfiguration mAmbientDisplayConfiguration;

    private KosmosJavaAdapter mKosmos;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mKosmos = new KosmosJavaAdapter(this);
        mDozeServiceHost = new DozeServiceHost(mDozeLog, mPowerManager, mWakefullnessLifecycle,
                mStatusBarStateController, mDeviceProvisionedController,
                mHeadsUpManager, mBatteryController, mScrimController,
                () -> mBiometricUnlockController, () -> mAssistManager, mDozeScrimController,
                mKeyguardUpdateMonitor, mPulseExpansionHandler, mNotificationShadeWindowController,
                mNotificationWakeUpCoordinator, mAuthController,
                mShadeLockscreenInteractor, mDozeInteractor);
                mShadeLockscreenInteractor, mDozeInteractor,
                mKosmos.getDeviceEntryFingerprintAuthInteractor(),
                mKosmos.getTestScope(), mContext, mAmbientDisplayConfiguration);

        mDozeServiceHost.initialize(
                mCentralSurfaces,
@@ -183,7 +201,8 @@ public class DozeServiceHostTest extends SysuiTestCase {
                Arrays.asList(DozeLog.REASON_SENSOR_PICKUP,
                        DozeLog.REASON_SENSOR_DOUBLE_TAP,
                        DozeLog.REASON_SENSOR_TAP,
                        DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS));
                        DozeLog.REASON_SENSOR_UDFPS_LONG_PRESS,
                        DozeLog.REASON_USUDFPS_PULSE));

        doAnswer(invocation -> {
            DozeHost.PulseCallback callback = invocation.getArgument(0);
@@ -239,4 +258,99 @@ public class DozeServiceHostTest extends SysuiTestCase {
        // THEN dozeInteractor's dozeTimeTick is updated
        verify(mDozeInteractor).dozeTimeTick();
    }

    @Test
    @EnableFlags(Flags.FLAG_UDFPS_SCREEN_OFF_UNLOCK_FLICKER)
    public void testCollectingUsUdfpsPulseEvents_shouldNotCollect() {
        // Should not collect pulse events if not usudfps.
        mKosmos.getFingerprintPropertyRepository().setProperties(
                0,
                SensorStrength.STRONG,
                FingerprintSensorType.UDFPS_OPTICAL,
                new HashMap<>()
        );
        mKosmos.getTestScope().getTestScheduler().runCurrent();
        when(mAmbientDisplayConfiguration.screenOffUdfpsEnabled(anyInt())).thenReturn(true);
        mDozeServiceHost.startDozing();
        assertFalse(mDozeServiceHost.isCollectingUsUdfpsScreenOffPulseEvents());
        mDozeServiceHost.stopDozing();

        // Should not collect pulse events if feature disabled.
        mKosmos.getFingerprintPropertyRepository().setProperties(
                0,
                SensorStrength.STRONG,
                FingerprintSensorType.UDFPS_ULTRASONIC,
                new HashMap<>()
        );
        mKosmos.getTestScope().getTestScheduler().runCurrent();
        when(mAmbientDisplayConfiguration.screenOffUdfpsEnabled(anyInt())).thenReturn(false);
        mDozeServiceHost.startDozing();
        assertFalse(mDozeServiceHost.isCollectingUsUdfpsScreenOffPulseEvents());
        mDozeServiceHost.stopDozing();
    }

    @Test
    @DisableFlags(Flags.FLAG_UDFPS_SCREEN_OFF_UNLOCK_FLICKER)
    public void testCollectingUsUdfpsPulseEvents_shouldNotCollect_flagDisabled() {
        // Should not collect pulse events if the bug flag is disabled.
        mKosmos.getFingerprintPropertyRepository().setProperties(
                0,
                SensorStrength.STRONG,
                FingerprintSensorType.UDFPS_ULTRASONIC,
                new HashMap<>()
        );
        mKosmos.getTestScope().getTestScheduler().runCurrent();
        when(mAmbientDisplayConfiguration.screenOffUdfpsEnabled(anyInt())).thenReturn(true);
        mDozeServiceHost.startDozing();
        assertFalse(mDozeServiceHost.isCollectingUsUdfpsScreenOffPulseEvents());
        mDozeServiceHost.stopDozing();
    }

    @Test
    @EnableFlags(Flags.FLAG_UDFPS_SCREEN_OFF_UNLOCK_FLICKER)
    public void testCollectingUsUdfpsPulseEvents() {
        mKosmos.getFingerprintPropertyRepository().setProperties(
                0,
                SensorStrength.STRONG,
                FingerprintSensorType.UDFPS_ULTRASONIC,
                new HashMap<>());
        mKosmos.getTestScope().getTestScheduler().runCurrent();

        when(mAmbientDisplayConfiguration.screenOffUdfpsEnabled(anyInt())).thenReturn(true);
        assertFalse(mDozeServiceHost.isCollectingUsUdfpsScreenOffPulseEvents());
        DozeHost.Callback cb = mock(DozeHost.Callback.class);
        mDozeServiceHost.addCallback(cb);
        mDozeServiceHost.startDozing();

        // Should collect pulse events if usudfps and feature enabled.
        assertTrue(mDozeServiceHost.isCollectingUsUdfpsScreenOffPulseEvents());

        // Send events that will trigger callback.
        mKosmos.getDeviceEntryFingerprintAuthRepository().setAuthenticationStatus(
                new HelpFingerprintAuthenticationStatus(-1, "Test help"));
        mKosmos.getTestScope().getTestScheduler().runCurrent();

        // Callback should be invoked.
        verify(cb).onUltrasonicUdfpsPulseWhileScreenOff(
                any(HelpFingerprintAuthenticationStatus.class));

        mKosmos.getDeviceEntryFingerprintAuthRepository().setAuthenticationStatus(
                FailFingerprintAuthenticationStatus.INSTANCE);
        mKosmos.getTestScope().getTestScheduler().runCurrent();

        verify(cb).onUltrasonicUdfpsPulseWhileScreenOff(
                FailFingerprintAuthenticationStatus.INSTANCE);

        mKosmos.getDeviceEntryFingerprintAuthRepository().setAuthenticationStatus(
                new ErrorFingerprintAuthenticationStatus(
                        -1, "Test error", System.nanoTime()));
        mKosmos.getTestScope().getTestScheduler().runCurrent();

        verify(cb).onUltrasonicUdfpsPulseWhileScreenOff(
                any(ErrorFingerprintAuthenticationStatus.class));

        mDozeServiceHost.stopDozing();
        assertFalse(mDozeServiceHost.isCollectingUsUdfpsScreenOffPulseEvents());
        mDozeServiceHost.removeCallback(cb);
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -31,6 +31,10 @@ enum class FingerprintSensorType {
        return (this == UDFPS_OPTICAL) || (this == UDFPS_ULTRASONIC)
    }

    fun isUltrasonic(): Boolean {
        return this == UDFPS_ULTRASONIC
    }

    fun isPowerButton(): Boolean {
        return this == POWER_BUTTON
    }
+27 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.deviceentry.domain.interactor
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.FailFingerprintAuthenticationStatus
@@ -26,10 +27,14 @@ import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatu
import com.android.systemui.keyguard.shared.model.HelpFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

@SysUISingleton
class DeviceEntryFingerprintAuthInteractor
@@ -38,6 +43,7 @@ constructor(
    repository: DeviceEntryFingerprintAuthRepository,
    biometricSettingsInteractor: DeviceEntryBiometricSettingsInteractor,
    fingerprintPropertyRepository: FingerprintPropertyRepository,
    @Application private val applicationScope: CoroutineScope,
) {
    /**
     * Whether fingerprint authentication is currently running or not. This does not mean the user
@@ -70,4 +76,25 @@ constructor(
     */
    val isSensorUnderDisplay =
        fingerprintPropertyRepository.sensorType.map(FingerprintSensorType::isUdfps)

    /** True if it is ultrasonic udfps sensor, otherwise false. */
    val isUltrasonic: StateFlow<Boolean> =
        fingerprintPropertyRepository.sensorType
            .map { it.isUltrasonic() }
            .stateIn(
                scope = applicationScope,
                started = SharingStarted.Eagerly,
                initialValue = fingerprintPropertyRepository.sensorType.value.isUltrasonic(),
            )

    /** Device entry fingerprint auth events that should turn on the display. */
    val fingerprintPulseEventsForDeviceEntry: Flow<FingerprintAuthenticationStatus> =
        repository.authenticationStatus.filter {
            when (it) {
                is HelpFingerprintAuthenticationStatus,
                is FailFingerprintAuthenticationStatus,
                is ErrorFingerprintAuthenticationStatus -> true
                else -> false
            }
        }
}
+15 −0
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import android.annotation.NonNull;

import androidx.annotation.MainThread;

import com.android.systemui.keyguard.shared.model.FingerprintAuthenticationStatus;

/**
 * Interface the doze service uses to communicate with the rest of system UI.
 */
@@ -104,6 +106,14 @@ public interface DozeHost {
     * wake-up gestures. */
    boolean isAlwaysOnSuppressed();

    /**
     * Whether we are collecting the usudfps authentication pulse events.
     * @return true if collecting the events, otherwise false.
     */
    default boolean isCollectingUsUdfpsScreenOffPulseEvents() {
        return false;
    }

    interface Callback {
        /**
         * Called when a high priority notification is added.
@@ -132,6 +142,11 @@ public interface DozeHost {
         * Called when fingerprint acquisition has started and screen state might need updating.
         */
        default void onSideFingerprintAcquisitionStarted() {}

        /**
         * Called when ultrasonic fingerprint auth events want the screen on to show info.
         */
        default void onUltrasonicUdfpsPulseWhileScreenOff(FingerprintAuthenticationStatus state) {}
    }

    interface PulseCallback {
Loading