Loading packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +2 −0 Original line number Diff line number Diff line Loading @@ -294,6 +294,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, } }); mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation); mUdfpsController.setUdfpsDisplayMode(new UdfpsDisplayMode(mContext, mExecution, this)); mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect(); } Loading packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +5 −3 Original line number Diff line number Diff line Loading @@ -123,7 +123,6 @@ public class UdfpsController implements DozeReceiver { @NonNull private final PowerManager mPowerManager; @NonNull private final AccessibilityManager mAccessibilityManager; @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; @Nullable private final UdfpsDisplayModeProvider mUdfpsDisplayMode; @NonNull private final ConfigurationController mConfigurationController; @NonNull private final SystemClock mSystemClock; @NonNull private final UnlockedScreenOffAnimationController Loading @@ -139,6 +138,7 @@ public class UdfpsController implements DozeReceiver { // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this. @Nullable private Runnable mAuthControllerUpdateUdfpsLocation; @Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider; @Nullable private UdfpsDisplayMode mUdfpsDisplayMode; // Tracks the velocity of a touch to help filter out the touches that move too fast. @Nullable private VelocityTracker mVelocityTracker; Loading Loading @@ -319,6 +319,10 @@ public class UdfpsController implements DozeReceiver { mAuthControllerUpdateUdfpsLocation = r; } public void setUdfpsDisplayMode(UdfpsDisplayMode udfpsDisplayMode) { mUdfpsDisplayMode = udfpsDisplayMode; } /** * Calculate the pointer speed given a velocity tracker and the pointer id. * This assumes that the velocity tracker has already been passed all relevant motion events. Loading Loading @@ -594,7 +598,6 @@ public class UdfpsController implements DozeReceiver { @NonNull VibratorHelper vibrator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, @NonNull UdfpsShell udfpsShell, @NonNull Optional<UdfpsDisplayModeProvider> udfpsDisplayMode, @NonNull KeyguardStateController keyguardStateController, @NonNull DisplayManager displayManager, @Main Handler mainHandler, Loading Loading @@ -626,7 +629,6 @@ public class UdfpsController implements DozeReceiver { mPowerManager = powerManager; mAccessibilityManager = accessibilityManager; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; mUdfpsDisplayMode = udfpsDisplayMode.orElse(null); screenLifecycle.addObserver(mScreenObserver); mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON; mConfigurationController = configurationController; Loading packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayMode.kt 0 → 100644 +88 −0 Original line number Diff line number Diff line package com.android.systemui.biometrics import android.content.Context import android.os.RemoteException import android.os.Trace import android.util.Log import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.concurrency.Execution import javax.inject.Inject private const val TAG = "UdfpsDisplayMode" /** * UdfpsDisplayMode that encapsulates pixel-specific code, such as enabling the high-brightness mode * (HBM) in a display-specific way and freezing the display's refresh rate. */ @SysUISingleton class UdfpsDisplayMode @Inject constructor( private val context: Context, private val execution: Execution, private val authController: AuthController ) : UdfpsDisplayModeProvider { // The request is reset to null after it's processed. private var currentRequest: Request? = null override fun enable(onEnabled: Runnable?) { execution.isMainThread() Log.v(TAG, "enable") if (currentRequest != null) { Log.e(TAG, "enable | already requested") return } if (authController.udfpsHbmListener == null) { Log.e(TAG, "enable | mDisplayManagerCallback is null") return } Trace.beginSection("UdfpsDisplayMode.enable") // Track this request in one object. val request = Request(context.displayId) currentRequest = request try { // This method is a misnomer. It has nothing to do with HBM, its purpose is to set // the appropriate display refresh rate. authController.udfpsHbmListener!!.onHbmEnabled(request.displayId) Log.v(TAG, "enable | requested optimal refresh rate for UDFPS") } catch (e: RemoteException) { Log.e(TAG, "enable", e) } onEnabled?.run() ?: Log.w(TAG, "enable | onEnabled is null") Trace.endSection() } override fun disable(onDisabled: Runnable?) { execution.isMainThread() Log.v(TAG, "disable") val request = currentRequest if (request == null) { Log.w(TAG, "disable | already disabled") return } Trace.beginSection("UdfpsDisplayMode.disable") try { // Allow DisplayManager to unset the UDFPS refresh rate. authController.udfpsHbmListener!!.onHbmDisabled(request.displayId) Log.v(TAG, "disable | removed the UDFPS refresh rate request") } catch (e: RemoteException) { Log.e(TAG, "disable", e) } currentRequest = null onDisabled?.run() ?: Log.w(TAG, "disable | onDisabled is null") Trace.endSection() } } /** Tracks a request to enable the UDFPS mode. */ private data class Request(val displayId: Int) packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -169,6 +169,8 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private LatencyTracker mLatencyTracker; private FakeExecutor mFgExecutor; @Mock private UdfpsDisplayMode mUdfpsDisplayMode; // Stuff for configuring mocks @Mock Loading Loading @@ -258,7 +260,6 @@ public class UdfpsControllerTest extends SysuiTestCase { mVibrator, mUdfpsHapticsSimulator, mUdfpsShell, Optional.of(mDisplayModeProvider), mKeyguardStateController, mDisplayManager, mHandler, Loading @@ -275,6 +276,7 @@ public class UdfpsControllerTest extends SysuiTestCase { verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); mScreenObserver = mScreenObserverCaptor.getValue(); mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, new UdfpsOverlayParams()); mUdfpsController.setUdfpsDisplayMode(mUdfpsDisplayMode); } @Test Loading packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java 0 → 100644 +132 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.biometrics; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.content.Context; import android.hardware.fingerprint.IUdfpsHbmListener; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.concurrency.FakeExecution; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) public class UdfpsDisplayModeTest extends SysuiTestCase { private static final int DISPLAY_ID = 0; @Mock private AuthController mAuthController; @Mock private IUdfpsHbmListener mDisplayCallback; @Mock private Runnable mOnEnabled; @Mock private Runnable mOnDisabled; private final FakeExecution mExecution = new FakeExecution(); private UdfpsDisplayMode mHbmController; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); // Force mContext to always return DISPLAY_ID Context contextSpy = spy(mContext); when(contextSpy.getDisplayId()).thenReturn(DISPLAY_ID); // Set up mocks. when(mAuthController.getUdfpsHbmListener()).thenReturn(mDisplayCallback); // Create a real controller with mock dependencies. mHbmController = new UdfpsDisplayMode(contextSpy, mExecution, mAuthController); } @Test public void roundTrip() throws RemoteException { // Enable the UDFPS mode. mHbmController.enable(mOnEnabled); // Should set the appropriate refresh rate for UDFPS and notify the caller. verify(mDisplayCallback).onHbmEnabled(eq(DISPLAY_ID)); verify(mOnEnabled).run(); // Disable the UDFPS mode. mHbmController.disable(mOnDisabled); // Should unset the refresh rate and notify the caller. verify(mOnDisabled).run(); verify(mDisplayCallback).onHbmDisabled(eq(DISPLAY_ID)); } @Test public void mustNotEnableMoreThanOnce() throws RemoteException { // First request to enable the UDFPS mode. mHbmController.enable(mOnEnabled); // Should set the appropriate refresh rate for UDFPS and notify the caller. verify(mDisplayCallback).onHbmEnabled(eq(DISPLAY_ID)); verify(mOnEnabled).run(); // Second request to enable the UDFPS mode, while it's still enabled. mHbmController.enable(mOnEnabled); // Should ignore the second request. verifyNoMoreInteractions(mDisplayCallback); verifyNoMoreInteractions(mOnEnabled); } @Test public void mustNotDisableMoreThanOnce() throws RemoteException { // Disable the UDFPS mode. mHbmController.enable(mOnEnabled); // Should set the appropriate refresh rate for UDFPS and notify the caller. verify(mDisplayCallback).onHbmEnabled(eq(DISPLAY_ID)); verify(mOnEnabled).run(); // First request to disable the UDFPS mode. mHbmController.disable(mOnDisabled); // Should unset the refresh rate and notify the caller. verify(mOnDisabled).run(); verify(mDisplayCallback).onHbmDisabled(eq(DISPLAY_ID)); // Second request to disable the UDFPS mode, when it's already disabled. mHbmController.disable(mOnDisabled); // Should ignore the second request. verifyNoMoreInteractions(mOnDisabled); verifyNoMoreInteractions(mDisplayCallback); } } Loading
packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +2 −0 Original line number Diff line number Diff line Loading @@ -294,6 +294,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, } }); mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation); mUdfpsController.setUdfpsDisplayMode(new UdfpsDisplayMode(mContext, mExecution, this)); mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect(); } Loading
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +5 −3 Original line number Diff line number Diff line Loading @@ -123,7 +123,6 @@ public class UdfpsController implements DozeReceiver { @NonNull private final PowerManager mPowerManager; @NonNull private final AccessibilityManager mAccessibilityManager; @NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; @Nullable private final UdfpsDisplayModeProvider mUdfpsDisplayMode; @NonNull private final ConfigurationController mConfigurationController; @NonNull private final SystemClock mSystemClock; @NonNull private final UnlockedScreenOffAnimationController Loading @@ -139,6 +138,7 @@ public class UdfpsController implements DozeReceiver { // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this. @Nullable private Runnable mAuthControllerUpdateUdfpsLocation; @Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider; @Nullable private UdfpsDisplayMode mUdfpsDisplayMode; // Tracks the velocity of a touch to help filter out the touches that move too fast. @Nullable private VelocityTracker mVelocityTracker; Loading Loading @@ -319,6 +319,10 @@ public class UdfpsController implements DozeReceiver { mAuthControllerUpdateUdfpsLocation = r; } public void setUdfpsDisplayMode(UdfpsDisplayMode udfpsDisplayMode) { mUdfpsDisplayMode = udfpsDisplayMode; } /** * Calculate the pointer speed given a velocity tracker and the pointer id. * This assumes that the velocity tracker has already been passed all relevant motion events. Loading Loading @@ -594,7 +598,6 @@ public class UdfpsController implements DozeReceiver { @NonNull VibratorHelper vibrator, @NonNull UdfpsHapticsSimulator udfpsHapticsSimulator, @NonNull UdfpsShell udfpsShell, @NonNull Optional<UdfpsDisplayModeProvider> udfpsDisplayMode, @NonNull KeyguardStateController keyguardStateController, @NonNull DisplayManager displayManager, @Main Handler mainHandler, Loading Loading @@ -626,7 +629,6 @@ public class UdfpsController implements DozeReceiver { mPowerManager = powerManager; mAccessibilityManager = accessibilityManager; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; mUdfpsDisplayMode = udfpsDisplayMode.orElse(null); screenLifecycle.addObserver(mScreenObserver); mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON; mConfigurationController = configurationController; Loading
packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayMode.kt 0 → 100644 +88 −0 Original line number Diff line number Diff line package com.android.systemui.biometrics import android.content.Context import android.os.RemoteException import android.os.Trace import android.util.Log import com.android.systemui.dagger.SysUISingleton import com.android.systemui.util.concurrency.Execution import javax.inject.Inject private const val TAG = "UdfpsDisplayMode" /** * UdfpsDisplayMode that encapsulates pixel-specific code, such as enabling the high-brightness mode * (HBM) in a display-specific way and freezing the display's refresh rate. */ @SysUISingleton class UdfpsDisplayMode @Inject constructor( private val context: Context, private val execution: Execution, private val authController: AuthController ) : UdfpsDisplayModeProvider { // The request is reset to null after it's processed. private var currentRequest: Request? = null override fun enable(onEnabled: Runnable?) { execution.isMainThread() Log.v(TAG, "enable") if (currentRequest != null) { Log.e(TAG, "enable | already requested") return } if (authController.udfpsHbmListener == null) { Log.e(TAG, "enable | mDisplayManagerCallback is null") return } Trace.beginSection("UdfpsDisplayMode.enable") // Track this request in one object. val request = Request(context.displayId) currentRequest = request try { // This method is a misnomer. It has nothing to do with HBM, its purpose is to set // the appropriate display refresh rate. authController.udfpsHbmListener!!.onHbmEnabled(request.displayId) Log.v(TAG, "enable | requested optimal refresh rate for UDFPS") } catch (e: RemoteException) { Log.e(TAG, "enable", e) } onEnabled?.run() ?: Log.w(TAG, "enable | onEnabled is null") Trace.endSection() } override fun disable(onDisabled: Runnable?) { execution.isMainThread() Log.v(TAG, "disable") val request = currentRequest if (request == null) { Log.w(TAG, "disable | already disabled") return } Trace.beginSection("UdfpsDisplayMode.disable") try { // Allow DisplayManager to unset the UDFPS refresh rate. authController.udfpsHbmListener!!.onHbmDisabled(request.displayId) Log.v(TAG, "disable | removed the UDFPS refresh rate request") } catch (e: RemoteException) { Log.e(TAG, "disable", e) } currentRequest = null onDisabled?.run() ?: Log.w(TAG, "disable | onDisabled is null") Trace.endSection() } } /** Tracks a request to enable the UDFPS mode. */ private data class Request(val displayId: Int)
packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +3 −1 Original line number Diff line number Diff line Loading @@ -169,6 +169,8 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private LatencyTracker mLatencyTracker; private FakeExecutor mFgExecutor; @Mock private UdfpsDisplayMode mUdfpsDisplayMode; // Stuff for configuring mocks @Mock Loading Loading @@ -258,7 +260,6 @@ public class UdfpsControllerTest extends SysuiTestCase { mVibrator, mUdfpsHapticsSimulator, mUdfpsShell, Optional.of(mDisplayModeProvider), mKeyguardStateController, mDisplayManager, mHandler, Loading @@ -275,6 +276,7 @@ public class UdfpsControllerTest extends SysuiTestCase { verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); mScreenObserver = mScreenObserverCaptor.getValue(); mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, new UdfpsOverlayParams()); mUdfpsController.setUdfpsDisplayMode(mUdfpsDisplayMode); } @Test Loading
packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsDisplayModeTest.java 0 → 100644 +132 −0 Original line number Diff line number Diff line /* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.biometrics; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.content.Context; import android.hardware.fingerprint.IUdfpsHbmListener; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.concurrency.FakeExecution; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) public class UdfpsDisplayModeTest extends SysuiTestCase { private static final int DISPLAY_ID = 0; @Mock private AuthController mAuthController; @Mock private IUdfpsHbmListener mDisplayCallback; @Mock private Runnable mOnEnabled; @Mock private Runnable mOnDisabled; private final FakeExecution mExecution = new FakeExecution(); private UdfpsDisplayMode mHbmController; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); // Force mContext to always return DISPLAY_ID Context contextSpy = spy(mContext); when(contextSpy.getDisplayId()).thenReturn(DISPLAY_ID); // Set up mocks. when(mAuthController.getUdfpsHbmListener()).thenReturn(mDisplayCallback); // Create a real controller with mock dependencies. mHbmController = new UdfpsDisplayMode(contextSpy, mExecution, mAuthController); } @Test public void roundTrip() throws RemoteException { // Enable the UDFPS mode. mHbmController.enable(mOnEnabled); // Should set the appropriate refresh rate for UDFPS and notify the caller. verify(mDisplayCallback).onHbmEnabled(eq(DISPLAY_ID)); verify(mOnEnabled).run(); // Disable the UDFPS mode. mHbmController.disable(mOnDisabled); // Should unset the refresh rate and notify the caller. verify(mOnDisabled).run(); verify(mDisplayCallback).onHbmDisabled(eq(DISPLAY_ID)); } @Test public void mustNotEnableMoreThanOnce() throws RemoteException { // First request to enable the UDFPS mode. mHbmController.enable(mOnEnabled); // Should set the appropriate refresh rate for UDFPS and notify the caller. verify(mDisplayCallback).onHbmEnabled(eq(DISPLAY_ID)); verify(mOnEnabled).run(); // Second request to enable the UDFPS mode, while it's still enabled. mHbmController.enable(mOnEnabled); // Should ignore the second request. verifyNoMoreInteractions(mDisplayCallback); verifyNoMoreInteractions(mOnEnabled); } @Test public void mustNotDisableMoreThanOnce() throws RemoteException { // Disable the UDFPS mode. mHbmController.enable(mOnEnabled); // Should set the appropriate refresh rate for UDFPS and notify the caller. verify(mDisplayCallback).onHbmEnabled(eq(DISPLAY_ID)); verify(mOnEnabled).run(); // First request to disable the UDFPS mode. mHbmController.disable(mOnDisabled); // Should unset the refresh rate and notify the caller. verify(mOnDisabled).run(); verify(mDisplayCallback).onHbmDisabled(eq(DISPLAY_ID)); // Second request to disable the UDFPS mode, when it's already disabled. mHbmController.disable(mOnDisabled); // Should ignore the second request. verifyNoMoreInteractions(mOnDisabled); verifyNoMoreInteractions(mDisplayCallback); } }