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
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index e6a88b1..05d26b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -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.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 @@
     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,7 +98,7 @@
     /**
      * 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.
@@ -89,6 +106,17 @@
     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.
      */
     private static final float BIOMETRIC_COLLAPSE_SPEEDUP_FACTOR = 1.1f;
@@ -240,8 +268,7 @@
         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 @@
             wakeUp.run();
         }
         switch (mMode) {
-            case MODE_UNLOCK:
-                Trace.beginSection("MODE_UNLOCK");
-                if (!wasDeviceInteractive) {
-                    mPendingShowBouncer = true;
-                } else {
-                    mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
-                            false /* strongAuth */);
-                }
+            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,49 +393,38 @@
         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) {
-                // 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_PULSING;
+            } else if (unlockingAllowed || !mUnlockMethodCache.isMethodSecure()) {
                 return MODE_WAKE_AND_UNLOCK;
-            } else if (face) {
-                if (!(mDozeScrimController.isPulsing() && !unlockingAllowed)) {
-                    Log.wtf(TAG, "Face somehow arrived when the device was not interactive");
-                }
-                if (faceStayingOnKeyguard) {
-                    // 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) {
+        if (unlockingAllowed && deviceDreaming) {
             return MODE_WAKE_AND_UNLOCK_FROM_DREAM;
         }
         if (mStatusBarKeyguardViewManager.isShowing()) {
-            if ((mStatusBarKeyguardViewManager.isBouncerShowing())
-                    && unlockingAllowed) {
-                return MODE_UNLOCK;
+            if (mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing() && unlockingAllowed) {
+                return MODE_DISMISS_BOUNCER;
             } else if (unlockingAllowed) {
-                return faceStayingOnKeyguard ? MODE_ONLY_WAKE : MODE_UNLOCK;
-            } else if (face) {
-                return faceStayingOnKeyguard ? MODE_NONE : MODE_SHOW_BOUNCER;
+                return MODE_UNLOCK_COLLAPSING;
             } else if (!mStatusBarKeyguardViewManager.isBouncerShowing()) {
                 return MODE_SHOW_BOUNCER;
             }
@@ -418,6 +432,49 @@
         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 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 (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;
+                }
+            }
+        }
+        if (unlockingAllowed && deviceDreaming) {
+            return bypass ? MODE_WAKE_AND_UNLOCK_FROM_DREAM : MODE_ONLY_WAKE;
+        }
+        if (mStatusBarKeyguardViewManager.isShowing()) {
+            if (mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing() && unlockingAllowed) {
+                return bypass && !mKeyguardBypassController.canPlaySubtleWindowAnimations()
+                        ? MODE_UNLOCK_COLLAPSING : MODE_UNLOCK_FADING;
+            } else if (unlockingAllowed) {
+                return bypass ? MODE_UNLOCK_FADING : MODE_NONE;
+            } else {
+                return bypass ? MODE_SHOW_BOUNCER : MODE_NONE;
+            }
+        }
+        return MODE_NONE;
+    }
+
     @Override
     public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
         mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH)
@@ -504,7 +561,7 @@
      * on or off.
      */
     public boolean isBiometricUnlock() {
-        return isWakeAndUnlock() || mMode == MODE_UNLOCK;
+        return isWakeAndUnlock() || mMode == MODE_UNLOCK_COLLAPSING || mMode == MODE_UNLOCK_FADING;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 4b198da..21a22ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -346,6 +346,13 @@
     }
 
     /**
+     * {@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.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index af23ac3..d7deedc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -138,6 +138,20 @@
         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
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 462b65f37..20e8e77 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -677,6 +677,14 @@
         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();
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 748f509..a99dc7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -82,6 +82,7 @@
         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 @@
                 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 @@
                 BiometricSourceType.FACE);
 
         verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
+        verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean());
     }
 
     @Test
@@ -151,6 +153,7 @@
         mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
                 BiometricSourceType.FACE);
 
+        verify(mStatusBarKeyguardViewManager, never()).animateCollapsePanels(anyFloat());
         verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
     }
 
@@ -181,7 +184,7 @@
     @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);