From 1e3ef210af8a53680b684e9ac6ecec83bcb8243c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wilczy=C5=84ski?= Date: Tue, 24 Jan 2023 15:02:17 +0000 Subject: [PATCH] Add follower DPC tests - change 'mHandler.sendMessage' to 'mHandler.sendMessageAtTime' so that 'mClock' is used instead of 'SystemClock' - move objects to the injector so that mocks can be injected Bug: 241307563 Test: atest DisplayPowerControllerTest Test: atest DisplayPowerController2Test Change-Id: I6cf7b64733cb3655034ab042fa9476e24ef27f00 --- .../display/DisplayPowerController.java | 77 +++- .../display/DisplayPowerController2.java | 77 +++- .../display/DisplayPowerController2Test.java | 368 ++++++++++++++--- .../display/DisplayPowerControllerTest.java | 379 ++++++++++++++---- 4 files changed, 740 insertions(+), 161 deletions(-) diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 40eec33716ce7..b58d907c87ebe 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1011,7 +1011,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } mBrightnessSettingListener = brightnessValue -> { Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); }; mBrightnessSetting.registerListener(mBrightnessSettingListener); @@ -1040,7 +1040,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final boolean isIdleScreenBrightnessEnabled = resources.getBoolean( R.bool.config_enableIdleScreenBrightnessMode); - mInteractiveModeBrightnessMapper = BrightnessMappingStrategy.create(resources, + mInteractiveModeBrightnessMapper = mInjector.getInteractiveModeBrightnessMapper(resources, mDisplayDeviceConfig, mDisplayWhiteBalanceController); if (isIdleScreenBrightnessEnabled) { mIdleModeBrightnessMapper = BrightnessMappingStrategy.createForIdleMode(resources, @@ -1065,7 +1065,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayDeviceConfig.getAmbientLuxDarkeningMinThreshold(); float ambientBrighteningMinThreshold = mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(); - HysteresisLevels ambientBrightnessThresholds = new HysteresisLevels( + HysteresisLevels ambientBrightnessThresholds = mInjector.getHysteresisLevels( ambientBrighteningThresholds, ambientDarkeningThresholds, ambientBrighteningLevels, ambientDarkeningLevels, ambientDarkeningMinThreshold, ambientBrighteningMinThreshold); @@ -1083,7 +1083,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayDeviceConfig.getScreenDarkeningMinThreshold(); float screenBrighteningMinThreshold = mDisplayDeviceConfig.getScreenBrighteningMinThreshold(); - HysteresisLevels screenBrightnessThresholds = new HysteresisLevels( + HysteresisLevels screenBrightnessThresholds = mInjector.getHysteresisLevels( screenBrighteningThresholds, screenDarkeningThresholds, screenBrighteningLevels, screenDarkeningLevels, screenDarkeningMinThreshold, screenBrighteningMinThreshold, true); @@ -1101,7 +1101,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayDeviceConfig.getAmbientBrighteningLevelsIdle(); float[] ambientDarkeningLevelsIdle = mDisplayDeviceConfig.getAmbientDarkeningLevelsIdle(); - HysteresisLevels ambientBrightnessThresholdsIdle = new HysteresisLevels( + HysteresisLevels ambientBrightnessThresholdsIdle = mInjector.getHysteresisLevels( ambientBrighteningThresholdsIdle, ambientDarkeningThresholdsIdle, ambientBrighteningLevelsIdle, ambientDarkeningLevelsIdle, ambientDarkeningMinThresholdIdle, ambientBrighteningMinThresholdIdle); @@ -1119,7 +1119,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mDisplayDeviceConfig.getScreenBrighteningLevelsIdle(); float[] screenDarkeningLevelsIdle = mDisplayDeviceConfig.getScreenDarkeningLevelsIdle(); - HysteresisLevels screenBrightnessThresholdsIdle = new HysteresisLevels( + HysteresisLevels screenBrightnessThresholdsIdle = mInjector.getHysteresisLevels( screenBrighteningThresholdsIdle, screenDarkeningThresholdsIdle, screenBrighteningLevelsIdle, screenDarkeningLevelsIdle, screenDarkeningMinThresholdIdle, screenBrighteningMinThresholdIdle); @@ -1155,8 +1155,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mAutomaticBrightnessController != null) { mAutomaticBrightnessController.stop(); } - mAutomaticBrightnessController = new AutomaticBrightnessController(this, - handler.getLooper(), mSensorManager, mLightSensor, + mAutomaticBrightnessController = mInjector.getAutomaticBrightnessController( + this, handler.getLooper(), mSensorManager, mLightSensor, mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounce, @@ -1257,7 +1257,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call public void onAnimationEnd() { sendUpdatePowerState(); Message msg = mHandler.obtainMessage(MSG_BRIGHTNESS_RAMP_DONE); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } }; @@ -2949,7 +2949,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call msg.what = MSG_STATSD_HBM_BRIGHTNESS; msg.arg1 = Float.floatToIntBits(brightness); msg.arg2 = mDisplayStatsId; - mHandler.sendMessageDelayed(msg, BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis() + + BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS); } } } @@ -3105,7 +3106,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void onScreenOn() { Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } } @@ -3113,7 +3114,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void onScreenOff() { Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } } @@ -3195,6 +3196,58 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call FloatProperty secondProperty) { return new DualRampAnimator(dps, firstProperty, secondProperty); } + + AutomaticBrightnessController getAutomaticBrightnessController( + AutomaticBrightnessController.Callbacks callbacks, Looper looper, + SensorManager sensorManager, Sensor lightSensor, + BrightnessMappingStrategy interactiveModeBrightnessMapper, + int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, + float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, + long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, + boolean resetAmbientLuxAfterWarmUpConfig, + HysteresisLevels ambientBrightnessThresholds, + HysteresisLevels screenBrightnessThresholds, + HysteresisLevels ambientBrightnessThresholdsIdle, + HysteresisLevels screenBrightnessThresholdsIdle, Context context, + HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler, + BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, + int ambientLightHorizonLong, float userLux, float userBrightness) { + return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor, + interactiveModeBrightnessMapper, lightSensorWarmUpTime, brightnessMin, + brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate, + brighteningLightDebounceConfig, darkeningLightDebounceConfig, + resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds, + screenBrightnessThresholds, ambientBrightnessThresholdsIdle, + screenBrightnessThresholdsIdle, context, hbmController, brightnessThrottler, + idleModeBrightnessMapper, ambientLightHorizonShort, ambientLightHorizonLong, + userLux, userBrightness); + } + + BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, + DisplayDeviceConfig displayDeviceConfig, + DisplayWhiteBalanceController displayWhiteBalanceController) { + return BrightnessMappingStrategy.create(resources, + displayDeviceConfig, displayWhiteBalanceController); + } + + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold) { + return new HysteresisLevels(brighteningThresholdsPercentages, + darkeningThresholdsPercentages, brighteningThresholdLevels, + darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold); + } + + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold, boolean potentialOldBrightnessRange) { + return new HysteresisLevels(brighteningThresholdsPercentages, + darkeningThresholdsPercentages, brighteningThresholdLevels, + darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold, + potentialOldBrightnessRange); + } } static class CachedBrightnessInfo { diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 6092ad7d7f138..23ef680a0292b 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -850,7 +850,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal BrightnessSetting.BrightnessSettingListener brightnessSettingListener = brightnessValue -> { Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); }; mDisplayBrightnessController .registerBrightnessSettingChangeListener(brightnessSettingListener); @@ -880,7 +880,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal final boolean isIdleScreenBrightnessEnabled = resources.getBoolean( R.bool.config_enableIdleScreenBrightnessMode); - mInteractiveModeBrightnessMapper = BrightnessMappingStrategy.create(resources, + mInteractiveModeBrightnessMapper = mInjector.getInteractiveModeBrightnessMapper(resources, mDisplayDeviceConfig, mDisplayWhiteBalanceController); if (isIdleScreenBrightnessEnabled) { mIdleModeBrightnessMapper = BrightnessMappingStrategy.createForIdleMode(resources, @@ -905,7 +905,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayDeviceConfig.getAmbientLuxDarkeningMinThreshold(); float ambientBrighteningMinThreshold = mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold(); - HysteresisLevels ambientBrightnessThresholds = new HysteresisLevels( + HysteresisLevels ambientBrightnessThresholds = mInjector.getHysteresisLevels( ambientBrighteningThresholds, ambientDarkeningThresholds, ambientBrighteningLevels, ambientDarkeningLevels, ambientDarkeningMinThreshold, ambientBrighteningMinThreshold); @@ -923,7 +923,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayDeviceConfig.getScreenDarkeningMinThreshold(); float screenBrighteningMinThreshold = mDisplayDeviceConfig.getScreenBrighteningMinThreshold(); - HysteresisLevels screenBrightnessThresholds = new HysteresisLevels( + HysteresisLevels screenBrightnessThresholds = mInjector.getHysteresisLevels( screenBrighteningThresholds, screenDarkeningThresholds, screenBrighteningLevels, screenDarkeningLevels, screenDarkeningMinThreshold, screenBrighteningMinThreshold, true); @@ -941,7 +941,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayDeviceConfig.getAmbientBrighteningLevelsIdle(); float[] ambientDarkeningLevelsIdle = mDisplayDeviceConfig.getAmbientDarkeningLevelsIdle(); - HysteresisLevels ambientBrightnessThresholdsIdle = new HysteresisLevels( + HysteresisLevels ambientBrightnessThresholdsIdle = mInjector.getHysteresisLevels( ambientBrighteningThresholdsIdle, ambientDarkeningThresholdsIdle, ambientBrighteningLevelsIdle, ambientDarkeningLevelsIdle, ambientDarkeningMinThresholdIdle, ambientBrighteningMinThresholdIdle); @@ -959,7 +959,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayDeviceConfig.getScreenBrighteningLevelsIdle(); float[] screenDarkeningLevelsIdle = mDisplayDeviceConfig.getScreenDarkeningLevelsIdle(); - HysteresisLevels screenBrightnessThresholdsIdle = new HysteresisLevels( + HysteresisLevels screenBrightnessThresholdsIdle = mInjector.getHysteresisLevels( screenBrighteningThresholdsIdle, screenDarkeningThresholdsIdle, screenBrighteningLevelsIdle, screenDarkeningLevelsIdle, screenDarkeningMinThresholdIdle, screenBrighteningMinThresholdIdle); @@ -995,8 +995,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal if (mAutomaticBrightnessController != null) { mAutomaticBrightnessController.stop(); } - mAutomaticBrightnessController = new AutomaticBrightnessController(this, - handler.getLooper(), mSensorManager, mLightSensor, + mAutomaticBrightnessController = mInjector.getAutomaticBrightnessController( + this, handler.getLooper(), mSensorManager, mLightSensor, mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounce, @@ -1094,7 +1094,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal public void onAnimationEnd() { sendUpdatePowerState(); Message msg = mHandler.obtainMessage(MSG_BRIGHTNESS_RAMP_DONE); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } }; @@ -2458,7 +2458,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal msg.what = MSG_STATSD_HBM_BRIGHTNESS; msg.arg1 = Float.floatToIntBits(brightness); msg.arg2 = mDisplayStatsId; - mHandler.sendMessageDelayed(msg, BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis() + + BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS); } } } @@ -2589,7 +2590,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal @Override public void onScreenOn() { Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } } @@ -2597,7 +2598,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal @Override public void onScreenOff() { Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } } @@ -2671,6 +2672,58 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal looper, nudgeUpdatePowerState, displayId, sensorManager, /* injector= */ null); } + + AutomaticBrightnessController getAutomaticBrightnessController( + AutomaticBrightnessController.Callbacks callbacks, Looper looper, + SensorManager sensorManager, Sensor lightSensor, + BrightnessMappingStrategy interactiveModeBrightnessMapper, + int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, + float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, + long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, + boolean resetAmbientLuxAfterWarmUpConfig, + HysteresisLevels ambientBrightnessThresholds, + HysteresisLevels screenBrightnessThresholds, + HysteresisLevels ambientBrightnessThresholdsIdle, + HysteresisLevels screenBrightnessThresholdsIdle, Context context, + HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler, + BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort, + int ambientLightHorizonLong, float userLux, float userBrightness) { + return new AutomaticBrightnessController(callbacks, looper, sensorManager, lightSensor, + interactiveModeBrightnessMapper, lightSensorWarmUpTime, brightnessMin, + brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate, + brighteningLightDebounceConfig, darkeningLightDebounceConfig, + resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds, + screenBrightnessThresholds, ambientBrightnessThresholdsIdle, + screenBrightnessThresholdsIdle, context, hbmController, brightnessThrottler, + idleModeBrightnessMapper, ambientLightHorizonShort, ambientLightHorizonLong, + userLux, userBrightness); + } + + BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, + DisplayDeviceConfig displayDeviceConfig, + DisplayWhiteBalanceController displayWhiteBalanceController) { + return BrightnessMappingStrategy.create(resources, + displayDeviceConfig, displayWhiteBalanceController); + } + + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold) { + return new HysteresisLevels(brighteningThresholdsPercentages, + darkeningThresholdsPercentages, brighteningThresholdLevels, + darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold); + } + + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold, boolean potentialOldBrightnessRange) { + return new HysteresisLevels(brighteningThresholdsPercentages, + darkeningThresholdsPercentages, brighteningThresholdLevels, + darkeningThresholdLevels, minDarkeningThreshold, minBrighteningThreshold, + potentialOldBrightnessRange); + } } static class CachedBrightnessInfo { diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java index 57e873d434ed5..5ca01eeb69dbe 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java @@ -19,13 +19,14 @@ package com.android.server.display; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -41,6 +42,7 @@ import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.os.Handler; import android.os.Looper; import android.os.PowerManager; +import android.os.SystemProperties; import android.os.test.TestLooper; import android.util.FloatProperty; import android.view.Display; @@ -55,6 +57,7 @@ import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; import com.android.server.display.color.ColorDisplayService; +import com.android.server.display.whitebalance.DisplayWhiteBalanceController; import com.android.server.policy.WindowManagerPolicy; import com.android.server.testutils.OffsettableClock; @@ -77,13 +80,18 @@ import java.util.List; public final class DisplayPowerController2Test { private static final String UNIQUE_DISPLAY_ID = "unique_id_test123"; private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY; + private static final int FOLLOWER_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1; private MockitoSession mSession; private OffsettableClock mClock; private TestLooper mTestLooper; private Handler mHandler; private DisplayPowerController2.Injector mInjector; + private DisplayPowerController2.Injector mFollowerInjector; private Context mContextSpy; + private DisplayPowerController2 mDpc; + private DisplayPowerController2 mFollowerDpc; + private Sensor mProxSensor; @Mock private DisplayPowerCallbacks mDisplayPowerCallbacksMock; @@ -94,14 +102,22 @@ public final class DisplayPowerController2Test { @Mock private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock; @Mock + private HighBrightnessModeMetadata mFollowerHighBrightnessModeMetadataMock; + @Mock private LogicalDisplay mLogicalDisplayMock; @Mock + private LogicalDisplay mFollowerLogicalDisplayMock; + @Mock private DisplayDevice mDisplayDeviceMock; @Mock + private DisplayDevice mFollowerDisplayDeviceMock; + @Mock private BrightnessTracker mBrightnessTrackerMock; @Mock private BrightnessSetting mBrightnessSettingMock; @Mock + private BrightnessSetting mFollowerBrightnessSettingMock; + @Mock private WindowManagerPolicy mWindowManagerPolicyMock; @Mock private PowerManager mPowerManagerMock; @@ -110,10 +126,22 @@ public final class DisplayPowerController2Test { @Mock private DisplayDeviceConfig mDisplayDeviceConfigMock; @Mock + private DisplayDeviceConfig mFollowerDisplayDeviceConfigMock; + @Mock private DisplayPowerState mDisplayPowerStateMock; @Mock private DualRampAnimator mDualRampAnimatorMock; @Mock + private DualRampAnimator mFollowerDualRampAnimatorMock; + @Mock + private AutomaticBrightnessController mAutomaticBrightnessControllerMock; + @Mock + private AutomaticBrightnessController mFollowerAutomaticBrightnessControllerMock; + @Mock + private BrightnessMappingStrategy mBrightnessMapperMock; + @Mock + private HysteresisLevels mHysteresisLevelsMock; + @Mock private WakelockController mWakelockController; @Mock private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock; @@ -126,6 +154,7 @@ public final class DisplayPowerController2Test { mSession = ExtendedMockito.mockitoSession() .initMocks(this) .strictness(Strictness.LENIENT) + .spyStatic(SystemProperties.class) .spyStatic(LocalServices.class) .spyStatic(BatteryStatsService.class) .startMocking(); @@ -167,6 +196,113 @@ public final class DisplayPowerController2Test { displayDeviceConfig, looper, nudgeUpdatePowerState, displayId, sensorManager, /* injector= */ null); } + + @Override + AutomaticBrightnessController getAutomaticBrightnessController( + AutomaticBrightnessController.Callbacks callbacks, Looper looper, + SensorManager sensorManager, Sensor lightSensor, + BrightnessMappingStrategy interactiveModeBrightnessMapper, + int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, + float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, + long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, + boolean resetAmbientLuxAfterWarmUpConfig, + HysteresisLevels ambientBrightnessThresholds, + HysteresisLevels screenBrightnessThresholds, + HysteresisLevels ambientBrightnessThresholdsIdle, + HysteresisLevels screenBrightnessThresholdsIdle, Context context, + HighBrightnessModeController hbmController, + BrightnessThrottler brightnessThrottler, + BrightnessMappingStrategy idleModeBrightnessMapper, + int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, + float userBrightness) { + return mAutomaticBrightnessControllerMock; + } + + @Override + BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, + DisplayDeviceConfig displayDeviceConfig, + DisplayWhiteBalanceController displayWhiteBalanceController) { + return mBrightnessMapperMock; + } + + @Override + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold) { + return mHysteresisLevelsMock; + } + + @Override + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold, boolean potentialOldBrightnessRange) { + return mHysteresisLevelsMock; + } + }; + mFollowerInjector = new DisplayPowerController2.Injector() { + @Override + DisplayPowerController2.Clock getClock() { + return mClock::now; + } + + @Override + DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, + int displayId, int displayState) { + return mDisplayPowerStateMock; + } + + @Override + DualRampAnimator getDualRampAnimator(DisplayPowerState dps, + FloatProperty firstProperty, + FloatProperty secondProperty) { + return mFollowerDualRampAnimatorMock; + } + + @Override + AutomaticBrightnessController getAutomaticBrightnessController( + AutomaticBrightnessController.Callbacks callbacks, Looper looper, + SensorManager sensorManager, Sensor lightSensor, + BrightnessMappingStrategy interactiveModeBrightnessMapper, + int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, + float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, + long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, + boolean resetAmbientLuxAfterWarmUpConfig, + HysteresisLevels ambientBrightnessThresholds, + HysteresisLevels screenBrightnessThresholds, + HysteresisLevels ambientBrightnessThresholdsIdle, + HysteresisLevels screenBrightnessThresholdsIdle, Context context, + HighBrightnessModeController hbmController, + BrightnessThrottler brightnessThrottler, + BrightnessMappingStrategy idleModeBrightnessMapper, + int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, + float userBrightness) { + return mFollowerAutomaticBrightnessControllerMock; + } + + @Override + BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, + DisplayDeviceConfig displayDeviceConfig, + DisplayWhiteBalanceController displayWhiteBalanceController) { + return mBrightnessMapperMock; + } + + @Override + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold) { + return mHysteresisLevelsMock; + } + + @Override + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold, boolean potentialOldBrightnessRange) { + return mHysteresisLevelsMock; + } }; addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock); @@ -174,11 +310,30 @@ public final class DisplayPowerController2Test { when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock); when(mContextSpy.getResources()).thenReturn(mResourcesMock); + doAnswer((Answer) invocationOnMock -> null).when(() -> + SystemProperties.set(anyString(), any())); doAnswer((Answer) invocationOnMock -> mCdsiMock).when(() -> LocalServices.getService( ColorDisplayService.ColorDisplayServiceInternal.class)); - doAnswer((Answer) invocationOnMock -> null).when(() -> - BatteryStatsService.getService()); + doAnswer((Answer) invocationOnMock -> null).when(BatteryStatsService::getService); + + setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID, mLogicalDisplayMock, mDisplayDeviceMock, + mDisplayDeviceConfigMock); + setUpDisplay(FOLLOWER_DISPLAY_ID, UNIQUE_DISPLAY_ID, mFollowerLogicalDisplayMock, + mFollowerDisplayDeviceMock, mFollowerDisplayDeviceConfigMock); + + mProxSensor = setUpProxSensor(); + + mDpc = new DisplayPowerController2( + mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler, + mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock, + mBrightnessTrackerMock, mBrightnessSettingMock, () -> { + }, mHighBrightnessModeMetadataMock); + mFollowerDpc = new DisplayPowerController2( + mContextSpy, mFollowerInjector, mDisplayPowerCallbacksMock, mHandler, + mSensorManagerMock, mDisplayBlankerMock, mFollowerLogicalDisplayMock, + mBrightnessTrackerMock, mFollowerBrightnessSettingMock, () -> { + }, mFollowerHighBrightnessModeMetadataMock); } @After @@ -189,30 +344,20 @@ public final class DisplayPowerController2Test { @Test public void testReleaseProxSuspendBlockersOnExit() throws Exception { - setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID); - - Sensor proxSensor = setUpProxSensor(); - - DisplayPowerController2 dpc = new DisplayPowerController2( - mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler, - mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock, - mBrightnessTrackerMock, mBrightnessSettingMock, () -> { - }, mHighBrightnessModeMetadataMock); - when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON); // send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; dpr.useProximitySensor = true; - dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); + mDpc.requestPowerState(dpr, false /* waitForNegativeProximity */); // Run updatePowerState to start listener for the prox sensor advanceTime(1); - SensorEventListener listener = getSensorEventListener(proxSensor); + SensorEventListener listener = getSensorEventListener(mProxSensor); assertNotNull(listener); - listener.onSensorChanged(TestUtils.createSensorEvent(proxSensor, 5 /* lux */)); + listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, 5 /* lux */)); advanceTime(1); // two times, one for unfinished business and one for proximity @@ -221,8 +366,7 @@ public final class DisplayPowerController2Test { verify(mWakelockController).acquireWakelock( WakelockController.WAKE_LOCK_PROXIMITY_DEBOUNCE); - - dpc.stop(); + mDpc.stop(); advanceTime(1); // two times, one for unfinished business and one for proximity verify(mWakelockController).acquireWakelock( @@ -232,29 +376,19 @@ public final class DisplayPowerController2Test { } @Test - public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() throws Exception { - setUpDisplay(1, UNIQUE_DISPLAY_ID); - - Sensor proxSensor = setUpProxSensor(); - - DisplayPowerController2 dpc = new DisplayPowerController2( - mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler, - mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock, - mBrightnessTrackerMock, mBrightnessSettingMock, () -> { - }, mHighBrightnessModeMetadataMock); - + public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() { when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON); // send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; dpr.useProximitySensor = true; - dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); + mFollowerDpc.requestPowerState(dpr, false /* waitForNegativeProximity */); // Run updatePowerState advanceTime(1); verify(mSensorManagerMock, never()).registerListener(any(SensorEventListener.class), - eq(proxSensor), anyInt(), any(Handler.class)); + eq(mProxSensor), anyInt(), any(Handler.class)); } /** @@ -284,56 +418,158 @@ public final class DisplayPowerController2Test { return mSensorEventListenerCaptor.getValue(); } - private void setUpDisplay(int displayId, String uniqueId) { + private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock, + DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock) { DisplayInfo info = new DisplayInfo(); DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); - when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); - when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock); - when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); - when(mLogicalDisplayMock.isEnabledLocked()).thenReturn(true); - when(mLogicalDisplayMock.isInTransitionLocked()).thenReturn(false); - when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); - when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId); - when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock); - when(mDisplayDeviceConfigMock.getProximitySensor()).thenReturn( + when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); + when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock); + when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); + when(logicalDisplayMock.isEnabledLocked()).thenReturn(true); + when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); + when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); + when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); + when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); + when(displayDeviceConfigMock.getProximitySensor()).thenReturn( new DisplayDeviceConfig.SensorData() { { type = Sensor.STRING_TYPE_PROXIMITY; name = null; } }); - when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500}); + when(displayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500}); + when(displayDeviceConfigMock.isAutoBrightnessAvailable()).thenReturn(true); + when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn( + new DisplayDeviceConfig.SensorData()); + when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn( + new DisplayDeviceConfig.SensorData()); } @Test - public void testDisplayBrightnessFollowers() { - setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID); + public void testDisplayBrightnessFollowers_BothDpcsSupportNits() { + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + mFollowerDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState - DisplayPowerController2 defaultDpc = new DisplayPowerController2( - mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler, - mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock, - mBrightnessTrackerMock, mBrightnessSettingMock, () -> { - }, mHighBrightnessModeMetadataMock); - DisplayPowerController2 followerDpc = new DisplayPowerController2( - mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler, - mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock, - mBrightnessTrackerMock, mBrightnessSettingMock, () -> { - }, mHighBrightnessModeMetadataMock); + ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); + verify(mBrightnessSettingMock).registerListener(listenerCaptor.capture()); + BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - defaultDpc.addDisplayBrightnessFollower(followerDpc); + mDpc.addDisplayBrightnessFollower(mFollowerDpc); - defaultDpc.setBrightness(0.3f); - assertEquals(defaultDpc.getBrightnessInfo().brightness, - followerDpc.getBrightnessInfo().brightness, 0); + // Test different float scale values + float leadBrightness = 0.3f; + float followerBrightness = 0.4f; + float nits = 300; + when(mAutomaticBrightnessControllerMock.convertToNits(leadBrightness)).thenReturn(nits); + when(mFollowerAutomaticBrightnessControllerMock.convertToFloatScale(nits)) + .thenReturn(followerBrightness); + when(mBrightnessSettingMock.getBrightness()).thenReturn(leadBrightness); + listener.onBrightnessChanged(leadBrightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(leadBrightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock).animateTo(eq(followerBrightness), anyFloat(), + anyFloat()); - defaultDpc.setBrightness(0.6f); - assertEquals(defaultDpc.getBrightnessInfo().brightness, - followerDpc.getBrightnessInfo().brightness, 0); + clearInvocations(mDualRampAnimatorMock, mFollowerDualRampAnimatorMock); - float brightness = 0.1f; - defaultDpc.clearDisplayBrightnessFollowers(); - defaultDpc.setBrightness(brightness); - assertNotEquals(brightness, followerDpc.getBrightnessInfo().brightness, 0); + // Test the same float scale value + float brightness = 0.6f; + nits = 600; + when(mAutomaticBrightnessControllerMock.convertToNits(brightness)).thenReturn(nits); + when(mFollowerAutomaticBrightnessControllerMock.convertToFloatScale(nits)) + .thenReturn(brightness); + when(mBrightnessSettingMock.getBrightness()).thenReturn(brightness); + listener.onBrightnessChanged(brightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + + clearInvocations(mDualRampAnimatorMock, mFollowerDualRampAnimatorMock); + + // Test clear followers + mDpc.clearDisplayBrightnessFollowers(); + when(mBrightnessSettingMock.getBrightness()).thenReturn(leadBrightness); + listener.onBrightnessChanged(leadBrightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(leadBrightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock, never()).animateTo(eq(followerBrightness), anyFloat(), + anyFloat()); + } + + @Test + public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() { + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + mFollowerDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); + verify(mBrightnessSettingMock).registerListener(listenerCaptor.capture()); + BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); + + mDpc.addDisplayBrightnessFollower(mFollowerDpc); + + float brightness = 0.3f; + when(mAutomaticBrightnessControllerMock.convertToNits(brightness)).thenReturn(300f); + when(mFollowerAutomaticBrightnessControllerMock.convertToFloatScale(anyFloat())) + .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); + when(mBrightnessSettingMock.getBrightness()).thenReturn(brightness); + listener.onBrightnessChanged(brightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + } + + @Test + public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() { + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + mFollowerDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); + verify(mBrightnessSettingMock).registerListener(listenerCaptor.capture()); + BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); + + mDpc.addDisplayBrightnessFollower(mFollowerDpc); + + float brightness = 0.3f; + when(mAutomaticBrightnessControllerMock.convertToNits(anyFloat())).thenReturn(-1f); + when(mBrightnessSettingMock.getBrightness()).thenReturn(brightness); + listener.onBrightnessChanged(brightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + } + + @Test + public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() { + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + mFollowerDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); + verify(mBrightnessSettingMock).registerListener(listenerCaptor.capture()); + BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); + + mDpc.addDisplayBrightnessFollower(mFollowerDpc); + + float brightness = 0.3f; + when(mAutomaticBrightnessControllerMock.convertToNits(anyFloat())).thenReturn(-1f); + when(mFollowerAutomaticBrightnessControllerMock.convertToFloatScale(anyFloat())) + .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); + when(mBrightnessSettingMock.getBrightness()).thenReturn(brightness); + listener.onBrightnessChanged(brightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); } } diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java index 6bf5b62ee8d69..996a9abb81fdb 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -19,14 +19,14 @@ package com.android.server.display; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -40,7 +40,9 @@ import android.hardware.SensorManager; import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.os.Handler; +import android.os.Looper; import android.os.PowerManager; +import android.os.SystemProperties; import android.os.test.TestLooper; import android.util.FloatProperty; import android.view.Display; @@ -55,6 +57,7 @@ import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; import com.android.server.display.color.ColorDisplayService; +import com.android.server.display.whitebalance.DisplayWhiteBalanceController; import com.android.server.policy.WindowManagerPolicy; import com.android.server.testutils.OffsettableClock; @@ -77,13 +80,18 @@ import java.util.List; public final class DisplayPowerControllerTest { private static final String UNIQUE_DISPLAY_ID = "unique_id_test123"; private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY; + private static final int FOLLOWER_DISPLAY_ID = Display.DEFAULT_DISPLAY + 1; private MockitoSession mSession; private OffsettableClock mClock; private TestLooper mTestLooper; private Handler mHandler; private DisplayPowerController.Injector mInjector; + private DisplayPowerController.Injector mFollowerInjector; private Context mContextSpy; + private DisplayPowerController mDpc; + private DisplayPowerController mFollowerDpc; + private Sensor mProxSensor; @Mock private DisplayPowerCallbacks mDisplayPowerCallbacksMock; @@ -94,14 +102,22 @@ public final class DisplayPowerControllerTest { @Mock private LogicalDisplay mLogicalDisplayMock; @Mock + private LogicalDisplay mFollowerLogicalDisplayMock; + @Mock private DisplayDevice mDisplayDeviceMock; @Mock + private DisplayDevice mFollowerDisplayDeviceMock; + @Mock private HighBrightnessModeMetadata mHighBrightnessModeMetadataMock; @Mock + private HighBrightnessModeMetadata mFollowerHighBrightnessModeMetadataMock; + @Mock private BrightnessTracker mBrightnessTrackerMock; @Mock private BrightnessSetting mBrightnessSettingMock; @Mock + private BrightnessSetting mFollowerBrightnessSettingMock; + @Mock private WindowManagerPolicy mWindowManagerPolicyMock; @Mock private PowerManager mPowerManagerMock; @@ -110,10 +126,22 @@ public final class DisplayPowerControllerTest { @Mock private DisplayDeviceConfig mDisplayDeviceConfigMock; @Mock + private DisplayDeviceConfig mFollowerDisplayDeviceConfigMock; + @Mock private DisplayPowerState mDisplayPowerStateMock; @Mock private DualRampAnimator mDualRampAnimatorMock; @Mock + private DualRampAnimator mFollowerDualRampAnimatorMock; + @Mock + private AutomaticBrightnessController mAutomaticBrightnessControllerMock; + @Mock + private AutomaticBrightnessController mFollowerAutomaticBrightnessControllerMock; + @Mock + private BrightnessMappingStrategy mBrightnessMapperMock; + @Mock + private HysteresisLevels mHysteresisLevelsMock; + @Mock private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock; @Captor @@ -124,6 +152,7 @@ public final class DisplayPowerControllerTest { mSession = ExtendedMockito.mockitoSession() .initMocks(this) .strictness(Strictness.LENIENT) + .spyStatic(SystemProperties.class) .spyStatic(LocalServices.class) .spyStatic(BatteryStatsService.class) .startMocking(); @@ -149,6 +178,113 @@ public final class DisplayPowerControllerTest { FloatProperty secondProperty) { return mDualRampAnimatorMock; } + + @Override + AutomaticBrightnessController getAutomaticBrightnessController( + AutomaticBrightnessController.Callbacks callbacks, Looper looper, + SensorManager sensorManager, Sensor lightSensor, + BrightnessMappingStrategy interactiveModeBrightnessMapper, + int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, + float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, + long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, + boolean resetAmbientLuxAfterWarmUpConfig, + HysteresisLevels ambientBrightnessThresholds, + HysteresisLevels screenBrightnessThresholds, + HysteresisLevels ambientBrightnessThresholdsIdle, + HysteresisLevels screenBrightnessThresholdsIdle, Context context, + HighBrightnessModeController hbmController, + BrightnessThrottler brightnessThrottler, + BrightnessMappingStrategy idleModeBrightnessMapper, + int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, + float userBrightness) { + return mAutomaticBrightnessControllerMock; + } + + @Override + BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, + DisplayDeviceConfig displayDeviceConfig, + DisplayWhiteBalanceController displayWhiteBalanceController) { + return mBrightnessMapperMock; + } + + @Override + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold) { + return mHysteresisLevelsMock; + } + + @Override + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold, boolean potentialOldBrightnessRange) { + return mHysteresisLevelsMock; + } + }; + mFollowerInjector = new DisplayPowerController.Injector() { + @Override + DisplayPowerController.Clock getClock() { + return mClock::now; + } + + @Override + DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, + int displayId, int displayState) { + return mDisplayPowerStateMock; + } + + @Override + DualRampAnimator getDualRampAnimator(DisplayPowerState dps, + FloatProperty firstProperty, + FloatProperty secondProperty) { + return mFollowerDualRampAnimatorMock; + } + + @Override + AutomaticBrightnessController getAutomaticBrightnessController( + AutomaticBrightnessController.Callbacks callbacks, Looper looper, + SensorManager sensorManager, Sensor lightSensor, + BrightnessMappingStrategy interactiveModeBrightnessMapper, + int lightSensorWarmUpTime, float brightnessMin, float brightnessMax, + float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, + long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, + boolean resetAmbientLuxAfterWarmUpConfig, + HysteresisLevels ambientBrightnessThresholds, + HysteresisLevels screenBrightnessThresholds, + HysteresisLevels ambientBrightnessThresholdsIdle, + HysteresisLevels screenBrightnessThresholdsIdle, Context context, + HighBrightnessModeController hbmController, + BrightnessThrottler brightnessThrottler, + BrightnessMappingStrategy idleModeBrightnessMapper, + int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, + float userBrightness) { + return mFollowerAutomaticBrightnessControllerMock; + } + + @Override + BrightnessMappingStrategy getInteractiveModeBrightnessMapper(Resources resources, + DisplayDeviceConfig displayDeviceConfig, + DisplayWhiteBalanceController displayWhiteBalanceController) { + return mBrightnessMapperMock; + } + + @Override + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold) { + return mHysteresisLevelsMock; + } + + @Override + HysteresisLevels getHysteresisLevels(float[] brighteningThresholdsPercentages, + float[] darkeningThresholdsPercentages, float[] brighteningThresholdLevels, + float[] darkeningThresholdLevels, float minDarkeningThreshold, + float minBrighteningThreshold, boolean potentialOldBrightnessRange) { + return mHysteresisLevelsMock; + } }; addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock); @@ -156,11 +292,30 @@ public final class DisplayPowerControllerTest { when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock); when(mContextSpy.getResources()).thenReturn(mResourcesMock); + doAnswer((Answer) invocationOnMock -> null).when(() -> + SystemProperties.set(anyString(), any())); doAnswer((Answer) invocationOnMock -> mCdsiMock).when(() -> LocalServices.getService( - ColorDisplayService.ColorDisplayServiceInternal.class)); - doAnswer((Answer) invocationOnMock -> null).when(() -> - BatteryStatsService.getService()); + ColorDisplayService.ColorDisplayServiceInternal.class)); + doAnswer((Answer) invocationOnMock -> null).when(BatteryStatsService::getService); + + setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID, mLogicalDisplayMock, mDisplayDeviceMock, + mDisplayDeviceConfigMock); + setUpDisplay(FOLLOWER_DISPLAY_ID, UNIQUE_DISPLAY_ID, mFollowerLogicalDisplayMock, + mFollowerDisplayDeviceMock, mFollowerDisplayDeviceConfigMock); + + mProxSensor = setUpProxSensor(); + + mDpc = new DisplayPowerController( + mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler, + mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock, + mBrightnessTrackerMock, mBrightnessSettingMock, () -> { + }, mHighBrightnessModeMetadataMock); + mFollowerDpc = new DisplayPowerController( + mContextSpy, mFollowerInjector, mDisplayPowerCallbacksMock, mHandler, + mSensorManagerMock, mDisplayBlankerMock, mFollowerLogicalDisplayMock, + mBrightnessTrackerMock, mFollowerBrightnessSettingMock, () -> { + }, mFollowerHighBrightnessModeMetadataMock); } @After @@ -171,72 +326,52 @@ public final class DisplayPowerControllerTest { @Test public void testReleaseProxSuspendBlockersOnExit() throws Exception { - setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID); - - Sensor proxSensor = setUpProxSensor(); - - DisplayPowerController dpc = new DisplayPowerController( - mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler, - mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock, - mBrightnessTrackerMock, mBrightnessSettingMock, () -> { - }, mHighBrightnessModeMetadataMock); - when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON); // send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; dpr.useProximitySensor = true; - dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); + mDpc.requestPowerState(dpr, false /* waitForNegativeProximity */); // Run updatePowerState to start listener for the prox sensor advanceTime(1); - SensorEventListener listener = getSensorEventListener(proxSensor); + SensorEventListener listener = getSensorEventListener(mProxSensor); assertNotNull(listener); - listener.onSensorChanged(TestUtils.createSensorEvent(proxSensor, 5 /* lux */)); + listener.onSensorChanged(TestUtils.createSensorEvent(mProxSensor, 5 /* lux */)); advanceTime(1); // two times, one for unfinished business and one for proximity verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker( - dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID)); + mDpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID)); verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker( - dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID)); + mDpc.getSuspendBlockerProxDebounceId(DISPLAY_ID)); - dpc.stop(); + mDpc.stop(); advanceTime(1); // two times, one for unfinished business and one for proximity verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker( - dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID)); + mDpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID)); verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker( - dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID)); + mDpc.getSuspendBlockerProxDebounceId(DISPLAY_ID)); } @Test - public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() throws Exception { - setUpDisplay(1, UNIQUE_DISPLAY_ID); - - Sensor proxSensor = setUpProxSensor(); - - DisplayPowerController dpc = new DisplayPowerController( - mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler, - mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock, - mBrightnessTrackerMock, mBrightnessSettingMock, () -> { - }, mHighBrightnessModeMetadataMock); - + public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() { when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON); // send a display power request DisplayPowerRequest dpr = new DisplayPowerRequest(); dpr.policy = DisplayPowerRequest.POLICY_BRIGHT; dpr.useProximitySensor = true; - dpc.requestPowerState(dpr, false /* waitForNegativeProximity */); + mFollowerDpc.requestPowerState(dpr, false /* waitForNegativeProximity */); // Run updatePowerState advanceTime(1); verify(mSensorManagerMock, never()).registerListener(any(SensorEventListener.class), - eq(proxSensor), anyInt(), any(Handler.class)); + eq(mProxSensor), anyInt(), any(Handler.class)); } /** @@ -266,56 +401,158 @@ public final class DisplayPowerControllerTest { return mSensorEventListenerCaptor.getValue(); } - private void setUpDisplay(int displayId, String uniqueId) { + private void setUpDisplay(int displayId, String uniqueId, LogicalDisplay logicalDisplayMock, + DisplayDevice displayDeviceMock, DisplayDeviceConfig displayDeviceConfigMock) { DisplayInfo info = new DisplayInfo(); DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo(); - when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); - when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock); - when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); - when(mLogicalDisplayMock.isEnabledLocked()).thenReturn(true); - when(mLogicalDisplayMock.isInTransitionLocked()).thenReturn(false); - when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); - when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId); - when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock); - when(mDisplayDeviceConfigMock.getProximitySensor()).thenReturn( + when(logicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId); + when(logicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(displayDeviceMock); + when(logicalDisplayMock.getDisplayInfoLocked()).thenReturn(info); + when(logicalDisplayMock.isEnabledLocked()).thenReturn(true); + when(logicalDisplayMock.isInTransitionLocked()).thenReturn(false); + when(displayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); + when(displayDeviceMock.getUniqueId()).thenReturn(uniqueId); + when(displayDeviceMock.getDisplayDeviceConfig()).thenReturn(displayDeviceConfigMock); + when(displayDeviceConfigMock.getProximitySensor()).thenReturn( new DisplayDeviceConfig.SensorData() { { type = Sensor.STRING_TYPE_PROXIMITY; name = null; } }); - when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500}); + when(displayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500}); + when(displayDeviceConfigMock.isAutoBrightnessAvailable()).thenReturn(true); + when(displayDeviceConfigMock.getAmbientLightSensor()).thenReturn( + new DisplayDeviceConfig.SensorData()); + when(displayDeviceConfigMock.getScreenOffBrightnessSensor()).thenReturn( + new DisplayDeviceConfig.SensorData()); } @Test - public void testDisplayBrightnessFollowers() { - setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID); + public void testDisplayBrightnessFollowers_BothDpcsSupportNits() { + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + mFollowerDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState - DisplayPowerController defaultDpc = new DisplayPowerController( - mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler, - mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock, - mBrightnessTrackerMock, mBrightnessSettingMock, () -> { - }, mHighBrightnessModeMetadataMock); - DisplayPowerController followerDpc = new DisplayPowerController( - mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler, - mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock, - mBrightnessTrackerMock, mBrightnessSettingMock, () -> { - }, mHighBrightnessModeMetadataMock); + ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); + verify(mBrightnessSettingMock).registerListener(listenerCaptor.capture()); + BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); - defaultDpc.addDisplayBrightnessFollower(followerDpc); + mDpc.addDisplayBrightnessFollower(mFollowerDpc); - defaultDpc.setBrightness(0.3f); - assertEquals(defaultDpc.getBrightnessInfo().brightness, - followerDpc.getBrightnessInfo().brightness, 0); + // Test different float scale values + float leadBrightness = 0.3f; + float followerBrightness = 0.4f; + float nits = 300; + when(mAutomaticBrightnessControllerMock.convertToNits(leadBrightness)).thenReturn(nits); + when(mFollowerAutomaticBrightnessControllerMock.convertToFloatScale(nits)) + .thenReturn(followerBrightness); + when(mBrightnessSettingMock.getBrightness()).thenReturn(leadBrightness); + listener.onBrightnessChanged(leadBrightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(leadBrightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock).animateTo(eq(followerBrightness), anyFloat(), + anyFloat()); - defaultDpc.setBrightness(0.6f); - assertEquals(defaultDpc.getBrightnessInfo().brightness, - followerDpc.getBrightnessInfo().brightness, 0); + clearInvocations(mDualRampAnimatorMock, mFollowerDualRampAnimatorMock); - float brightness = 0.1f; - defaultDpc.clearDisplayBrightnessFollowers(); - defaultDpc.setBrightness(brightness); - assertNotEquals(brightness, followerDpc.getBrightnessInfo().brightness, 0); + // Test the same float scale value + float brightness = 0.6f; + nits = 600; + when(mAutomaticBrightnessControllerMock.convertToNits(brightness)).thenReturn(nits); + when(mFollowerAutomaticBrightnessControllerMock.convertToFloatScale(nits)) + .thenReturn(brightness); + when(mBrightnessSettingMock.getBrightness()).thenReturn(brightness); + listener.onBrightnessChanged(brightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + + clearInvocations(mDualRampAnimatorMock, mFollowerDualRampAnimatorMock); + + // Test clear followers + mDpc.clearDisplayBrightnessFollowers(); + when(mBrightnessSettingMock.getBrightness()).thenReturn(leadBrightness); + listener.onBrightnessChanged(leadBrightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(leadBrightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock, never()).animateTo(eq(followerBrightness), anyFloat(), + anyFloat()); + } + + @Test + public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() { + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + mFollowerDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); + verify(mBrightnessSettingMock).registerListener(listenerCaptor.capture()); + BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); + + mDpc.addDisplayBrightnessFollower(mFollowerDpc); + + float brightness = 0.3f; + when(mAutomaticBrightnessControllerMock.convertToNits(brightness)).thenReturn(300f); + when(mFollowerAutomaticBrightnessControllerMock.convertToFloatScale(anyFloat())) + .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); + when(mBrightnessSettingMock.getBrightness()).thenReturn(brightness); + listener.onBrightnessChanged(brightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + } + + @Test + public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() { + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + mFollowerDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); + verify(mBrightnessSettingMock).registerListener(listenerCaptor.capture()); + BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); + + mDpc.addDisplayBrightnessFollower(mFollowerDpc); + + float brightness = 0.3f; + when(mAutomaticBrightnessControllerMock.convertToNits(anyFloat())).thenReturn(-1f); + when(mBrightnessSettingMock.getBrightness()).thenReturn(brightness); + listener.onBrightnessChanged(brightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + } + + @Test + public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() { + DisplayPowerRequest dpr = new DisplayPowerRequest(); + mDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + mFollowerDpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false); + advanceTime(1); // Run updatePowerState + + ArgumentCaptor listenerCaptor = + ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class); + verify(mBrightnessSettingMock).registerListener(listenerCaptor.capture()); + BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue(); + + mDpc.addDisplayBrightnessFollower(mFollowerDpc); + + float brightness = 0.3f; + when(mAutomaticBrightnessControllerMock.convertToNits(anyFloat())).thenReturn(-1f); + when(mFollowerAutomaticBrightnessControllerMock.convertToFloatScale(anyFloat())) + .thenReturn(PowerManager.BRIGHTNESS_INVALID_FLOAT); + when(mBrightnessSettingMock.getBrightness()).thenReturn(brightness); + listener.onBrightnessChanged(brightness); + advanceTime(1); // Send messages, run updatePowerState + verify(mDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); + verify(mFollowerDualRampAnimatorMock).animateTo(eq(brightness), anyFloat(), anyFloat()); } }