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

Commit fa817a09 authored by Lucas Dupin's avatar Lucas Dupin
Browse files

Wake-and-unlock fixes

- Created separate path for fingerprint devices
- Restored NPV animation for fingerprint
- Created keyguard bypass mode
- Fixed issue where bypass wouldn't dismiss lock screen

Fixes: 136568545
Bug: 134952761
Test: atest BiometricUnlockControllerTest
Test: unlock with SIM PIN
Test: face unlock from LS (no bypass)
Test: face unlock from AOD2 (no bypass)
Test: face unlock from bouncer (no bypass)
Test: face unlock from shade (no bypass)
Test: face unlock from LS (bypass)
Test: face unlock from AOD2 (bypass)
Test: face unlock from bouncer (bypass)
Test: face unlock from shade (bypass)
Test: fingerprint unlock from encrypted LS
Test: fingerprint unlock from LS
Test: fingerprint unlock from AOD2
Test: fingerprint unlock from bouncer
Test: fingerprint unlock from shade

Change-Id: I28052dcb7d9701beb1eeecb6312e55e96d748614
parent 95a52f63
Loading
Loading
Loading
Loading
+93 −36
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.statusbar.phone;

import android.annotation.IntDef;
import android.content.Context;
import android.hardware.biometrics.BiometricSourceType;
import android.metrics.LogMaker;
@@ -39,6 +40,8 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.statusbar.NotificationMediaManager;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Controller which coordinates all the biometric unlocking actions with the UI.
@@ -50,6 +53,20 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
    private static final long BIOMETRIC_WAKELOCK_TIMEOUT_MS = 15 * 1000;
    private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock wakelock";

    @IntDef(prefix = { "MODE_" }, value = {
            MODE_NONE,
            MODE_WAKE_AND_UNLOCK,
            MODE_WAKE_AND_UNLOCK_PULSING,
            MODE_SHOW_BOUNCER,
            MODE_ONLY_WAKE,
            MODE_UNLOCK_COLLAPSING,
            MODE_UNLOCK_FADING,
            MODE_DISMISS_BOUNCER,
            MODE_WAKE_AND_UNLOCK_FROM_DREAM
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface WakeAndUnlockMode {}

    /**
     * Mode in which we don't need to wake up the device when we authenticate.
     */
@@ -81,13 +98,24 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
    /**
     * Mode in which fingerprint unlocks the device.
     */
    public static final int MODE_UNLOCK = 5;
    public static final int MODE_UNLOCK_COLLAPSING = 5;

    /**
     * Mode in which fingerprint wakes and unlocks the device from a dream.
     */
    public static final int MODE_WAKE_AND_UNLOCK_FROM_DREAM = 6;

    /**
     * Faster mode of dismissing the lock screen when we cross fade to an app
     * (used for keyguard bypass.)
     */
    public static final int MODE_UNLOCK_FADING = 7;

    /**
     * When bouncer is visible and will be dismissed.
     */
    public static final int MODE_DISMISS_BOUNCER = 8;

    /**
     * How much faster we collapse the lockscreen when authenticating with biometric.
     */
@@ -240,8 +268,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
        startWakeAndUnlock(calculateMode(biometricSourceType));
    }

    public void startWakeAndUnlock(int mode) {
        // TODO(b/62444020): remove when this bug is fixed
    public void startWakeAndUnlock(@WakeAndUnlockMode int mode) {
        Log.v(TAG, "startWakeAndUnlock(" + mode + ")");
        boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
        mMode = mode;
@@ -277,18 +304,16 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
            wakeUp.run();
        }
        switch (mMode) {
            case MODE_UNLOCK:
                Trace.beginSection("MODE_UNLOCK");
                if (!wasDeviceInteractive) {
                    mPendingShowBouncer = true;
                } else {
            case MODE_DISMISS_BOUNCER:
            case MODE_UNLOCK_FADING:
                Trace.beginSection("MODE_DISMISS_BOUNCER or MODE_UNLOCK_FADING");
                mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
                        false /* strongAuth */);
                }
                Trace.endSection();
                break;
            case MODE_UNLOCK_COLLAPSING:
            case MODE_SHOW_BOUNCER:
                Trace.beginSection("MODE_SHOW_BOUNCER");
                Trace.beginSection("MODE_UNLOCK_COLLAPSING or MODE_SHOW_BOUNCER");
                if (!wasDeviceInteractive) {
                    mPendingShowBouncer = true;
                } else {
@@ -368,51 +393,83 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
        return mMode;
    }

    private int calculateMode(BiometricSourceType biometricSourceType) {
    private @WakeAndUnlockMode int calculateMode(BiometricSourceType biometricSourceType) {
        if (biometricSourceType == BiometricSourceType.FACE
                || biometricSourceType == BiometricSourceType.IRIS) {
            return calculateModeForPassiveAuth();
        } else {
            return calculateModeForFingerprint();
        }
    }

    private @WakeAndUnlockMode int calculateModeForFingerprint() {
        boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
        boolean deviceDreaming = mUpdateMonitor.isDreaming();
        boolean face = biometricSourceType == BiometricSourceType.FACE;
        boolean faceStayingOnKeyguard = face && !mKeyguardBypassController.getBypassEnabled();

        if (!mUpdateMonitor.isDeviceInteractive()) {
            if (!mStatusBarKeyguardViewManager.isShowing()) {
                return MODE_ONLY_WAKE;
            } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
                return MODE_WAKE_AND_UNLOCK_PULSING;
            } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
                return MODE_WAKE_AND_UNLOCK;
            } else {
                return MODE_SHOW_BOUNCER;
            }
        }
        if (unlockingAllowed && deviceDreaming) {
            return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
        }
        if (mStatusBarKeyguardViewManager.isShowing()) {
            if (mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing() && unlockingAllowed) {
                return MODE_DISMISS_BOUNCER;
            } else if (unlockingAllowed) {
                return MODE_UNLOCK_COLLAPSING;
            } else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
                return MODE_SHOW_BOUNCER;
            }
        }
        return MODE_NONE;
    }

    private @WakeAndUnlockMode int calculateModeForPassiveAuth() {
        boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
        boolean deviceDreaming = mUpdateMonitor.isDreaming();
        boolean bypass = mKeyguardBypassController.getBypassEnabled();

        if (!mUpdateMonitor.isDeviceInteractive()) {
            if (!mStatusBarKeyguardViewManager.isShowing()) {
                return bypass ? MODE_WAKE_AND_UNLOCK : MODE_ONLY_WAKE;
            } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
                // Let's not wake-up to lock screen when not bypassing, otherwise the notification
                // would move as the user tried to tap it.
                return faceStayingOnKeyguard ? MODE_NONE : MODE_WAKE_AND_UNLOCK_PULSING;
            } else if (!face && (unlockingAllowed || !mUnlockMethodCache.isMethodSecure())) {
                return MODE_WAKE_AND_UNLOCK;
            } else if (face) {
                return bypass ? MODE_WAKE_AND_UNLOCK_PULSING : MODE_NONE;
            } else {
                if (!(mDozeScrimController.isPulsing() && !unlockingAllowed)) {
                    Log.wtf(TAG, "Face somehow arrived when the device was not interactive");
                }
                if (faceStayingOnKeyguard) {
                if (bypass) {
                    // Wake-up fading out nicely
                    return MODE_WAKE_AND_UNLOCK_PULSING;
                } else {
                    // We could theoretically return MODE_NONE, but this means that the device
                    // would be not interactive, unlocked, and the user would not see the device
                    // state.
                    return MODE_ONLY_WAKE;
                } else {
                    // Wake-up fading out nicely
                    return MODE_WAKE_AND_UNLOCK_PULSING;
                }
            } else {
                return MODE_SHOW_BOUNCER;
            }
        }
        if (unlockingAllowed && deviceDreaming && !faceStayingOnKeyguard) {
            return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
        if (unlockingAllowed && deviceDreaming) {
            return bypass ? MODE_WAKE_AND_UNLOCK_FROM_DREAM : MODE_ONLY_WAKE;
        }
        if (mStatusBarKeyguardViewManager.isShowing()) {
            if ((mStatusBarKeyguardViewManager.isBouncerShowing())
                    && unlockingAllowed) {
                return MODE_UNLOCK;
            if (mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing() && unlockingAllowed) {
                return bypass && !mKeyguardBypassController.canPlaySubtleWindowAnimations()
                        ? MODE_UNLOCK_COLLAPSING : MODE_UNLOCK_FADING;
            } else if (unlockingAllowed) {
                return faceStayingOnKeyguard ? MODE_ONLY_WAKE : MODE_UNLOCK;
            } else if (face) {
                return faceStayingOnKeyguard ? MODE_NONE : MODE_SHOW_BOUNCER;
            } else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
                return MODE_SHOW_BOUNCER;
                return bypass ? MODE_UNLOCK_FADING : MODE_NONE;
            } else {
                return bypass ? MODE_SHOW_BOUNCER : MODE_NONE;
            }
        }
        return MODE_NONE;
@@ -504,7 +561,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback {
     * on or off.
     */
    public boolean isBiometricUnlock() {
        return isWakeAndUnlock() || mMode == MODE_UNLOCK;
        return isWakeAndUnlock() || mMode == MODE_UNLOCK_COLLAPSING || mMode == MODE_UNLOCK_FADING;
    }

    /**
+7 −0
Original line number Diff line number Diff line
@@ -345,6 +345,13 @@ public class KeyguardBouncer {
                && mExpansion == EXPANSION_VISIBLE && !isAnimatingAway();
    }

    /**
     * {@link #show(boolean)} was called but we're not showing yet.
     */
    public boolean willShowSoon() {
        return mShowingSoon;
    }

    /**
     * @return {@code true} when bouncer's pre-hide animation already started but isn't completely
     *         hidden yet, {@code false} otherwise.
+14 −0
Original line number Diff line number Diff line
@@ -138,6 +138,20 @@ class KeyguardBypassController {
        return false
    }

    /**
     * If shorter animations should be played when unlocking.
     */
    fun canPlaySubtleWindowAnimations(): Boolean {
        if (bypassEnabled) {
            return when {
                statusBarStateController.state != StatusBarState.KEYGUARD -> false
                qSExpanded -> false
                else -> true
            }
        }
        return false
    }

    fun onStartedGoingToSleep() {
        pendingUnlockType = null
    }
+8 −0
Original line number Diff line number Diff line
@@ -677,6 +677,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
        return mBouncer.isShowing();
    }

    /**
     * When bouncer is fully visible or {@link KeyguardBouncer#show(boolean)} was called but
     * animation didn't finish yet.
     */
    public boolean bouncerIsOrWillBeShowing() {
        return mBouncer.isShowing() || mBouncer.willShowSoon();
    }

    public boolean isFullscreenBouncer() {
        return mBouncer.isFullscreenBouncer();
    }
+6 −3
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
        when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
        when(mUnlockMethodCache.isUnlockingWithFacePossible()).thenReturn(true);
        when(mKeyguardBypassController.onBiometricAuthenticated(any())).thenReturn(true);
        when(mKeyguardBypassController.canPlaySubtleWindowAnimations()).thenReturn(true);
        mContext.addMockSystemService(PowerManager.class, mPowerManager);
        mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
        mDependency.injectTestDependency(StatusBarWindowController.class,
@@ -120,13 +121,13 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
                BiometricSourceType.FINGERPRINT);

        verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
        verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
        verify(mStatusBarKeyguardViewManager).animateCollapsePanels(anyFloat());
    }

    @Test
    public void onBiometricAuthenticated_whenFingerprintOnBouncer_dismissBouncer() {
        when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
        when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                BiometricSourceType.FINGERPRINT);

@@ -140,6 +141,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
                BiometricSourceType.FACE);

        verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
        verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean());
    }

    @Test
@@ -151,6 +153,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                BiometricSourceType.FACE);

        verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
        verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
    }

@@ -181,7 +184,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
    @Test
    public void onBiometricAuthenticated_whenFaceOnBouncer_dismissBouncer() {
        when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
        when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
        when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
        mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                BiometricSourceType.FACE);