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);