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

Commit d9b98ab1 authored by Beverly's avatar Beverly Committed by Beverly Tai
Browse files

RESTRICT AUTOMERGE SFPS: Don't play auth haptics if power button pressed

On devices with side FPS enrolled:
* If the power button recently woke up the device,
don't play biometric success haptics
* Biometric failure haptics always play
* Success haptics still play if there was no
recent power button wakeup

Test: atest BiometricsUnlockControllerTest
Fixes: 263152685
Change-Id: I015793b5347a036dfc8f13063db3510a0878b78f
parent 11a96206
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.time.SystemClock;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -63,6 +64,7 @@ public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observe

    private final Context mContext;
    private final DisplayMetrics mDisplayMetrics;
    private final SystemClock mSystemClock;

    @Nullable
    private final IWallpaperManager mWallpaperManagerService;
@@ -71,6 +73,9 @@ public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observe

    private @PowerManager.WakeReason int mLastWakeReason = PowerManager.WAKE_REASON_UNKNOWN;

    public static final long UNKNOWN_LAST_WAKE_TIME = -1;
    private long mLastWakeTime = UNKNOWN_LAST_WAKE_TIME;

    @Nullable
    private Point mLastWakeOriginLocation = null;

@@ -84,10 +89,12 @@ public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observe
    public WakefulnessLifecycle(
            Context context,
            @Nullable IWallpaperManager wallpaperManagerService,
            SystemClock systemClock,
            DumpManager dumpManager) {
        mContext = context;
        mDisplayMetrics = context.getResources().getDisplayMetrics();
        mWallpaperManagerService = wallpaperManagerService;
        mSystemClock = systemClock;

        dumpManager.registerDumpable(getClass().getSimpleName(), this);
    }
@@ -103,6 +110,14 @@ public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observe
        return mLastWakeReason;
    }

    /**
     * Returns the most recent time (in device uptimeMillis) the display woke up.
     * Returns {@link UNKNOWN_LAST_WAKE_TIME} if there hasn't been a wakeup yet.
     */
    public long getLastWakeTime() {
        return mLastWakeTime;
    }

    /**
     * Returns the most recent reason the device went to sleep up. This is one of
     * PowerManager.GO_TO_SLEEP_REASON_*.
@@ -117,6 +132,7 @@ public class WakefulnessLifecycle extends Lifecycle<WakefulnessLifecycle.Observe
        }
        setWakefulness(WAKEFULNESS_WAKING);
        mLastWakeReason = pmWakeReason;
        mLastWakeTime = mSystemClock.uptimeMillis();
        updateLastWakeOriginLocation();

        if (mWallpaperManagerService != null) {
+34 −8
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.phone;

import static android.app.StatusBarManager.SESSION_KEYGUARD;

import static com.android.systemui.keyguard.WakefulnessLifecycle.UNKNOWN_LAST_WAKE_TIME;

import android.annotation.IntDef;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricFaceConstants;
@@ -27,7 +29,6 @@ import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.Trace;

import androidx.annotation.Nullable;
@@ -62,6 +63,7 @@ import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.time.SystemClock;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -78,6 +80,7 @@ import javax.inject.Inject;
 */
@SysUISingleton
public class BiometricUnlockController extends KeyguardUpdateMonitorCallback implements Dumpable {
    private static final long RECENT_POWER_BUTTON_PRESS_THRESHOLD_MS = 400L;
    private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
    private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
    private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
@@ -169,9 +172,11 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
    private final MetricsLogger mMetricsLogger;
    private final AuthController mAuthController;
    private final StatusBarStateController mStatusBarStateController;
    private final WakefulnessLifecycle mWakefulnessLifecycle;
    private final LatencyTracker mLatencyTracker;
    private final VibratorHelper mVibratorHelper;
    private final BiometricUnlockLogger mLogger;
    private final SystemClock mSystemClock;

    private long mLastFpFailureUptimeMillis;
    private int mNumConsecutiveFpFailures;
@@ -279,14 +284,17 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
            SessionTracker sessionTracker,
            LatencyTracker latencyTracker,
            ScreenOffAnimationController screenOffAnimationController,
            VibratorHelper vibrator) {
            VibratorHelper vibrator,
            SystemClock systemClock
    ) {
        mPowerManager = powerManager;
        mShadeController = shadeController;
        mUpdateMonitor = keyguardUpdateMonitor;
        mUpdateMonitor.registerCallback(this);
        mMediaManager = notificationMediaManager;
        mLatencyTracker = latencyTracker;
        wakefulnessLifecycle.addObserver(mWakefulnessObserver);
        mWakefulnessLifecycle = wakefulnessLifecycle;
        mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
        screenLifecycle.addObserver(mScreenObserver);

        mNotificationShadeWindowController = notificationShadeWindowController;
@@ -306,6 +314,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
        mScreenOffAnimationController = screenOffAnimationController;
        mVibratorHelper = vibrator;
        mLogger = biometricUnlockLogger;
        mSystemClock = systemClock;

        dumpManager.registerDumpable(getClass().getName(), this);
    }
@@ -429,8 +438,11 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
        Runnable wakeUp = ()-> {
            if (!wasDeviceInteractive || mUpdateMonitor.isDreaming()) {
                mLogger.i("bio wakelock: Authenticated, waking up...");
                mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_BIOMETRIC,
                        "android.policy:BIOMETRIC");
                mPowerManager.wakeUp(
                        mSystemClock.uptimeMillis(),
                        PowerManager.WAKE_REASON_BIOMETRIC,
                        "android.policy:BIOMETRIC"
                );
            }
            Trace.beginSection("release wake-and-unlock");
            releaseBiometricWakeLock();
@@ -670,7 +682,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
            startWakeAndUnlock(MODE_ONLY_WAKE);
        } else if (biometricSourceType == BiometricSourceType.FINGERPRINT
                && mUpdateMonitor.isUdfpsSupported()) {
            long currUptimeMillis = SystemClock.uptimeMillis();
            long currUptimeMillis = mSystemClock.uptimeMillis();
            if (currUptimeMillis - mLastFpFailureUptimeMillis < mConsecutiveFpFailureThreshold) {
                mNumConsecutiveFpFailures += 1;
            } else {
@@ -720,10 +732,24 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp

    // these haptics are for device-entry only
    private void vibrateSuccess(BiometricSourceType type) {
        if (mAuthController.isSfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())
                && lastWakeupFromPowerButtonWithinHapticThreshold()) {
            mLogger.d("Skip auth success haptic. Power button was recently pressed.");
            return;
        }
        mVibratorHelper.vibrateAuthSuccess(
                getClass().getSimpleName() + ", type =" + type + "device-entry::success");
    }

    private boolean lastWakeupFromPowerButtonWithinHapticThreshold() {
        final boolean lastWakeupFromPowerButton = mWakefulnessLifecycle.getLastWakeReason()
                == PowerManager.WAKE_REASON_POWER_BUTTON;
        return lastWakeupFromPowerButton
                && mWakefulnessLifecycle.getLastWakeTime() != UNKNOWN_LAST_WAKE_TIME
                && mSystemClock.uptimeMillis() - mWakefulnessLifecycle.getLastWakeTime()
                < RECENT_POWER_BUTTON_PRESS_THRESHOLD_MS;
    }

    private void vibrateError(BiometricSourceType type) {
        mVibratorHelper.vibrateAuthError(
                getClass().getSimpleName() + ", type =" + type + "device-entry::error");
@@ -816,7 +842,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
        if (mUpdateMonitor.isUdfpsSupported()) {
            pw.print("   mNumConsecutiveFpFailures="); pw.println(mNumConsecutiveFpFailures);
            pw.print("   time since last failure=");
            pw.println(SystemClock.uptimeMillis() - mLastFpFailureUptimeMillis);
            pw.println(mSystemClock.uptimeMillis() - mLastFpFailureUptimeMillis);
        }
    }

+7 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.util.time.FakeSystemClock;

import org.junit.Before;
import org.junit.Test;
@@ -51,7 +52,12 @@ public class WakefulnessLifecycleTest extends SysuiTestCase {
    public void setUp() throws Exception {
        mWallpaperManager = mock(IWallpaperManager.class);
        mWakefulness =
                new WakefulnessLifecycle(mContext, mWallpaperManager, mock(DumpManager.class));
                new WakefulnessLifecycle(
                        mContext,
                        mWallpaperManager,
                        new FakeSystemClock(),
                        mock(DumpManager.class)
                );
        mWakefulnessObserver = mock(WakefulnessLifecycle.Observer.class);
        mWakefulness.addObserver(mWakefulnessObserver);
    }
+87 −2
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.systemui.statusbar.phone;

import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.ArgumentMatchers.any;
@@ -57,6 +59,7 @@ import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.time.FakeSystemClock;

import org.junit.Before;
import org.junit.Test;
@@ -122,6 +125,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
    private VibratorHelper mVibratorHelper;
    @Mock
    private BiometricUnlockLogger mLogger;
    private final FakeSystemClock mSystemClock = new FakeSystemClock();
    private BiometricUnlockController mBiometricUnlockController;

    @Before
@@ -144,7 +148,9 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
                mMetricsLogger, mDumpManager, mPowerManager, mLogger,
                mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
                mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController,
                mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper);
                mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper,
                mSystemClock
        );
        mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
        mBiometricUnlockController.addBiometricModeListener(mBiometricModeListener);
        when(mUpdateMonitor.getStrongAuthTracker()).thenReturn(mStrongAuthTracker);
@@ -207,7 +213,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {

        verify(mKeyguardViewMediator).onWakeAndUnlocking();
        assertThat(mBiometricUnlockController.getMode())
                .isEqualTo(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
                .isEqualTo(MODE_WAKE_AND_UNLOCK);
    }

    @Test
@@ -457,4 +463,83 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
        // THEN wakeup the device
        verify(mPowerManager).wakeUp(anyLong(), anyInt(), anyString());
    }

    @Test
    public void onSideFingerprintSuccess_recentPowerButtonPress_noHaptic() {
        // GIVEN side fingerprint enrolled, last wake reason was power button
        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
        when(mWakefulnessLifecycle.getLastWakeReason())
                .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);

        // GIVEN last wake time just occurred
        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());

        // WHEN biometric fingerprint succeeds
        givenFingerprintModeUnlockCollapsing();
        mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
                true);

        // THEN DO NOT vibrate the device
        verify(mVibratorHelper, never()).vibrateAuthSuccess(anyString());
    }

    @Test
    public void onSideFingerprintSuccess_oldPowerButtonPress_playHaptic() {
        // GIVEN side fingerprint enrolled, last wake reason was power button
        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
        when(mWakefulnessLifecycle.getLastWakeReason())
                .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);

        // GIVEN last wake time was 500ms ago
        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());
        mSystemClock.advanceTime(500);

        // WHEN biometric fingerprint succeeds
        givenFingerprintModeUnlockCollapsing();
        mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
                true);

        // THEN vibrate the device
        verify(mVibratorHelper).vibrateAuthSuccess(anyString());
    }

    @Test
    public void onSideFingerprintSuccess_recentGestureWakeUp_playHaptic() {
        // GIVEN side fingerprint enrolled, wakeup just happened
        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());

        // GIVEN last wake reason was from a gesture
        when(mWakefulnessLifecycle.getLastWakeReason())
                .thenReturn(PowerManager.WAKE_REASON_GESTURE);

        // WHEN biometric fingerprint succeeds
        givenFingerprintModeUnlockCollapsing();
        mBiometricUnlockController.startWakeAndUnlock(BiometricSourceType.FINGERPRINT,
                true);

        // THEN vibrate the device
        verify(mVibratorHelper).vibrateAuthSuccess(anyString());
    }

    @Test
    public void onSideFingerprintFail_alwaysPlaysHaptic() {
        // GIVEN side fingerprint enrolled, last wake reason was recent power button
        when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
        when(mWakefulnessLifecycle.getLastWakeReason())
                .thenReturn(PowerManager.WAKE_REASON_POWER_BUTTON);
        when(mWakefulnessLifecycle.getLastWakeTime()).thenReturn(mSystemClock.uptimeMillis());

        // WHEN biometric fingerprint fails
        mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);

        // THEN always vibrate the device
        verify(mVibratorHelper).vibrateAuthError(anyString());
    }

    private void givenFingerprintModeUnlockCollapsing() {
        when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
        when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
        when(mKeyguardStateController.isShowing()).thenReturn(true);
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -380,7 +380,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
        }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any());

        mWakefulnessLifecycle =
                new WakefulnessLifecycle(mContext, mIWallpaperManager, mDumpManager);
                new WakefulnessLifecycle(mContext, mIWallpaperManager, mFakeSystemClock,
                        mDumpManager);
        mWakefulnessLifecycle.dispatchStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
        mWakefulnessLifecycle.dispatchFinishedWakingUp();