diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index ecbd82ca1589c..e243e1e76c54b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -65,6 +65,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -131,6 +132,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { // mode. private boolean mIsAodInterruptActive; @Nullable private Runnable mCancelAodTimeoutAction; + private boolean mScreenOn; + private Runnable mAodInterruptRunnable; private static final AudioAttributes VIBRATION_SONIFICATION_ATTRIBUTES = new AudioAttributes.Builder() @@ -155,6 +158,22 @@ public class UdfpsController implements DozeReceiver, HbmCallback { } }; + private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { + @Override + public void onScreenTurnedOn() { + mScreenOn = true; + if (mAodInterruptRunnable != null) { + mAodInterruptRunnable.run(); + mAodInterruptRunnable = null; + } + } + + @Override + public void onScreenTurnedOff() { + mScreenOn = false; + } + }; + /** * Keeps track of state within a single FingerprintService request. Note that this state * persists across configuration changes, etc, since it is considered a single request. @@ -438,7 +457,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { @NonNull KeyguardViewMediator keyguardViewMediator, @NonNull FalsingManager falsingManager, @NonNull PowerManager powerManager, - @NonNull AccessibilityManager accessibilityManager) { + @NonNull AccessibilityManager accessibilityManager, + @NonNull ScreenLifecycle screenLifecycle) { mContext = context; // TODO (b/185124905): inject main handler and vibrator once done prototyping mMainHandler = new Handler(Looper.getMainLooper()); @@ -458,6 +478,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { mFalsingManager = falsingManager; mPowerManager = powerManager; mAccessibilityManager = accessibilityManager; + screenLifecycle.addObserver(mScreenObserver); + mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON; mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists @@ -691,14 +713,23 @@ public class UdfpsController implements DozeReceiver, HbmCallback { if (mIsAodInterruptActive) { return; } - mIsAodInterruptActive = true; - // Since the sensor that triggers the AOD interrupt doesn't provide ACTION_UP/ACTION_CANCEL, - // we need to be careful about not letting the screen accidentally remain in high brightness - // mode. As a mitigation, queue a call to cancel the fingerprint scan. - mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelUdfps, - AOD_INTERRUPT_TIMEOUT_MILLIS); - // using a hard-coded value for major and minor until it is available from the sensor - onFingerDown(screenX, screenY, minor, major); + + mAodInterruptRunnable = () -> { + mIsAodInterruptActive = true; + // Since the sensor that triggers the AOD interrupt doesn't provide + // ACTION_UP/ACTION_CANCEL, we need to be careful about not letting the screen + // accidentally remain in high brightness mode. As a mitigation, queue a call to + // cancel the fingerprint scan. + mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelUdfps, + AOD_INTERRUPT_TIMEOUT_MILLIS); + // using a hard-coded value for major and minor until it is available from the sensor + onFingerDown(screenX, screenY, minor, major); + }; + + if (mScreenOn && mAodInterruptRunnable != null) { + mAodInterruptRunnable.run(); + mAodInterruptRunnable = null; + } } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 40c48514f159b..aed49a84ca4d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -21,6 +21,7 @@ import static junit.framework.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -48,6 +49,7 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -110,6 +112,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private PowerManager mPowerManager; @Mock private AccessibilityManager mAccessibilityManager; + @Mock + private ScreenLifecycle mScreenLifecycle; private FakeExecutor mFgExecutor; @@ -126,6 +130,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private IUdfpsOverlayController mOverlayController; @Captor private ArgumentCaptor mTouchListenerCaptor; @Captor private ArgumentCaptor mOnIlluminatedRunnableCaptor; + @Captor private ArgumentCaptor mScreenObserverCaptor; + private ScreenLifecycle.Observer mScreenObserver; @Before public void setUp() { @@ -163,9 +169,12 @@ public class UdfpsControllerTest extends SysuiTestCase { mKeyguardViewMediator, mFalsingManager, mPowerManager, - mAccessibilityManager); + mAccessibilityManager, + mScreenLifecycle); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); + verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); + mScreenObserver = mScreenObserverCaptor.getValue(); assertEquals(TEST_UDFPS_SENSOR_ID, mUdfpsController.mSensorProps.sensorId); } @@ -233,9 +242,10 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void aodInterrupt() throws RemoteException { - // GIVEN that the overlay is showing + // GIVEN that the overlay is showing and screen is on mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); + mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); // WHEN fingerprint is requested because of AOD interrupt mUdfpsController.onAodInterrupt(0, 0, 2f, 3f); @@ -252,6 +262,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN AOD interrupt mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); + mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); // WHEN it is cancelled @@ -265,6 +276,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN AOD interrupt mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); + mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); // WHEN it times out @@ -273,4 +285,19 @@ public class UdfpsControllerTest extends SysuiTestCase { // THEN the illumination is hidden verify(mUdfpsView).stopIllumination(); } + + @Test + public void aodInterruptScreenOff() throws RemoteException { + // GIVEN screen off + mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); + mScreenObserver.onScreenTurnedOff(); + mFgExecutor.runAllReady(); + + // WHEN aod interrupt is received + mUdfpsController.onAodInterrupt(0, 0, 0f, 0f); + + // THEN no illumination because screen is off + verify(mUdfpsView, never()).startIllumination(any()); + } }