diff options
4 files changed, 357 insertions, 61 deletions
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index b3434805ee41..2dd3864a9505 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -2583,8 +2583,8 @@ public final class DisplayManagerService extends SystemService { final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore, display, mSyncRoot); final DisplayPowerController displayPowerController = new DisplayPowerController( - mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager, - mDisplayBlanker, display, mBrightnessTracker, brightnessSetting, + mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler, + mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting, () -> handleBrightnessChange(display)); mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController); } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 4e33fd07c28e..393875a11e91 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -48,6 +48,7 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; +import android.util.FloatProperty; import android.util.Log; import android.util.MathUtils; import android.util.MutableFloat; @@ -58,6 +59,7 @@ import android.view.Display; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IBatteryStats; import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.logging.MetricsLogger; @@ -97,7 +99,7 @@ import java.io.PrintWriter; * blocker as long as the display is not ready. So most of the work done here * does not need to worry about holding a suspend blocker unless it happens * independently of the display ready signal. - * + * * For debugging, you can make the color fade and brightness animations run * slower by changing the "animator duration scale" option in Development Settings. */ @@ -256,6 +258,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // to reach the final state. private final boolean mBrightnessBucketsInDozeConfig; + private final Clock mClock; + private final Injector mInjector; + // Maximum time a ramp animation can take. private long mBrightnessRampIncreaseMaxTimeMillis; private long mBrightnessRampDecreaseMaxTimeMillis; @@ -495,20 +500,22 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call /** * Creates the display power controller. */ - public DisplayPowerController(Context context, + DisplayPowerController(Context context, Injector injector, DisplayPowerCallbacks callbacks, Handler handler, SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay, BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting, Runnable onBrightnessChangeRunnable) { + + mInjector = injector != null ? injector : new Injector(); + mClock = mInjector.getClock(); mLogicalDisplay = logicalDisplay; mDisplayId = mLogicalDisplay.getDisplayIdLocked(); - final String displayIdStr = "[" + mDisplayId + "]"; - TAG = "DisplayPowerController" + displayIdStr; - mSuspendBlockerIdUnfinishedBusiness = displayIdStr + "unfinished business"; - mSuspendBlockerIdOnStateChanged = displayIdStr + "on state changed"; - mSuspendBlockerIdProxPositive = displayIdStr + "prox positive"; - mSuspendBlockerIdProxNegative = displayIdStr + "prox negative"; - mSuspendBlockerIdProxDebounce = displayIdStr + "prox debounce"; + TAG = "DisplayPowerController[" + mDisplayId + "]"; + mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId); + mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId); + mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId); + mSuspendBlockerIdProxNegative = getSuspendBlockerProxNegativeId(mDisplayId); + mSuspendBlockerIdProxDebounce = getSuspendBlockerProxDebounceId(mDisplayId); mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked(); mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId(); @@ -681,7 +688,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call /** * Get the {@link BrightnessChangeEvent}s for the specified user. - * @param userId userId to fetch data for + * + * @param userId userId to fetch data for * @param includePackage if false will null out the package name in events */ @Nullable @@ -723,10 +731,11 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call * The controller makes a copy of the provided object and then * begins adjusting the power state to match what was requested. * - * @param request The requested power state. + * @param request The requested power state. * @param waitForNegativeProximity If true, issues a request to wait for - * negative proximity before turning the screen back on, assuming the screen - * was turned off by the proximity sensor. + * negative proximity before turning the screen back on, + * assuming the screen + * was turned off by the proximity sensor. * @return True if display is ready, false if there are important changes that must * be made asynchronously (such as turning the screen on), in which case the caller * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()} @@ -834,7 +843,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call synchronized (mLock) { mStopped = true; Message msg = mHandler.obtainMessage(MSG_STOP); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); if (mDisplayWhiteBalanceController != null) { mDisplayWhiteBalanceController.setEnabled(false); @@ -888,12 +897,12 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (!mStopped && !mPendingUpdatePowerStateLocked) { mPendingUpdatePowerStateLocked = true; Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE); - mHandler.sendMessage(msg); + mHandler.sendMessageAtTime(msg, mClock.uptimeMillis()); } } private void initialize(int displayState) { - mPowerState = new DisplayPowerState(mBlanker, + mPowerState = mInjector.getDisplayPowerState(mBlanker, mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState); if (mColorFadeEnabled) { @@ -908,7 +917,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mColorFadeOffAnimator.addListener(mAnimatorListener); } - mScreenBrightnessRampAnimator = new DualRampAnimator<>(mPowerState, + mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState, DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT, DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT); mScreenBrightnessRampAnimator.setAnimationTimeLimits( @@ -1077,13 +1086,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void onAnimationStart(Animator animation) { } + @Override public void onAnimationEnd(Animator animation) { sendUpdatePowerState(); } + @Override public void onAnimationRepeat(Animator animation) { } + @Override public void onAnimationCancel(Animator animation) { } @@ -1124,8 +1136,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mOnProximityNegativeMessages = 0; final float brightness = mPowerState != null - ? mPowerState.getScreenBrightness() - : PowerManager.BRIGHTNESS_MIN; + ? mPowerState.getScreenBrightness() + : PowerManager.BRIGHTNESS_MIN; reportStats(brightness); if (mPowerState != null) { @@ -1203,7 +1215,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call state = Display.STATE_ON; break; } - assert(state != Display.STATE_UNKNOWN); + assert (state != Display.STATE_UNKNOWN); // Apply the proximity sensor. if (mProximitySensor != null) { @@ -1290,16 +1302,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call final boolean autoBrightnessEnabledInDoze = mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state); final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness - && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) - && Float.isNaN(brightnessState) - && mAutomaticBrightnessController != null; + && (state == Display.STATE_ON || autoBrightnessEnabledInDoze) + && Float.isNaN(brightnessState) + && mAutomaticBrightnessController != null; final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness - && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze); + && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze); final int autoBrightnessState = autoBrightnessEnabled ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED : autoBrightnessDisabledDueToDisplayOff - ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE - : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; + ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE + : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; final boolean userSetBrightnessChanged = updateUserSetScreenBrightness(); @@ -1589,9 +1601,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // AND if we are not in idle screen brightness mode. if (!brightnessIsTemporary && (mAutomaticBrightnessController != null - && !mAutomaticBrightnessController.isInIdleMode())) { + && !mAutomaticBrightnessController.isInIdleMode())) { if (userInitiatedChange && (mAutomaticBrightnessController == null - || !mAutomaticBrightnessController.hasValidAmbientLux())) { + || !mAutomaticBrightnessController.hasValidAmbientLux())) { // If we don't have a valid lux reading we can't report a valid // slider event so notify as if the system changed the brightness. userInitiatedChange = false; @@ -1781,26 +1793,26 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call boolean changed = false; changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness, - brightness); + mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness, + brightness); changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness, - adjustedBrightness); + mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness, + adjustedBrightness); changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin, - minBrightness); + mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin, + minBrightness); changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax, - maxBrightness); + mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax, + maxBrightness); changed |= - mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode, - mHbmController.getHighBrightnessMode()); + mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode, + mHbmController.getHighBrightnessMode()); changed |= - mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint, - mHbmController.getTransitionPoint()); + mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint, + mHbmController.getTransitionPoint()); changed |= - mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason, - mBrightnessThrottler.getBrightnessMaxReason()); + mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason, + mBrightnessThrottler.getBrightnessMaxReason()); return changed; } @@ -1942,7 +1954,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } if (!isOff && (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF - || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) { + || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) { setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON); if (mPowerState.getColorFadeLevel() == 0.0f) { blockScreenOn(); @@ -2070,7 +2082,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } else if (mPowerState.prepareColorFade(mContext, mColorFadeFadesConfig ? ColorFade.MODE_FADE : - ColorFade.MODE_WARM_UP)) { + ColorFade.MODE_WARM_UP)) { mColorFadeOnAnimator.start(); } else { mColorFadeOnAnimator.end(); @@ -2172,8 +2184,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mPowerState.dismissColorFadeResources(); } else if (performScreenOffTransition && mPowerState.prepareColorFade(mContext, - mColorFadeFadesConfig ? - ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN) + mColorFadeFadesConfig + ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN) && mPowerState.getScreenState() != Display.STATE_OFF) { // Perform the screen off animation. mColorFadeOffAnimator.start(); @@ -2244,7 +2256,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mProximitySensorEnabled && mPendingProximity != PROXIMITY_UNKNOWN && mPendingProximityDebounceTime >= 0) { - final long now = SystemClock.uptimeMillis(); + final long now = mClock.uptimeMillis(); if (mPendingProximityDebounceTime <= now) { if (mProximity != mPendingProximity) { // if the status of the sensor changed, stop ignoring. @@ -2522,7 +2534,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call pw.println(" mCachedBrightnessInfo.hbmTransitionPoint=" + mCachedBrightnessInfo.hbmTransitionPoint.value); pw.println(" mCachedBrightnessInfo.brightnessMaxReason =" + - mCachedBrightnessInfo.brightnessMaxReason .value); + mCachedBrightnessInfo.brightnessMaxReason.value); } pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig); pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig); @@ -2705,7 +2717,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX; - synchronized(mCachedBrightnessInfo) { + synchronized (mCachedBrightnessInfo) { if (mCachedBrightnessInfo.hbmTransitionPoint == null) { return; } @@ -2739,8 +2751,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } - - private final class DisplayControllerHandler extends Handler { public DisplayControllerHandler(Looper looper) { super(looper, null, true /*async*/); @@ -2805,7 +2815,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call break; case MSG_BRIGHTNESS_RAMP_DONE: - if (mPowerState != null) { + if (mPowerState != null) { final float brightness = mPowerState.getScreenBrightness(); reportStats(brightness); } @@ -2822,7 +2832,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call @Override public void onSensorChanged(SensorEvent event) { if (mProximitySensorEnabled) { - final long time = SystemClock.uptimeMillis(); + final long time = mClock.uptimeMillis(); final float distance = event.values[0]; boolean positive = distance >= 0.0f && distance < mProximityThreshold; handleProximitySensorEvent(time, positive); @@ -2891,19 +2901,68 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } } + @VisibleForTesting + String getSuspendBlockerUnfinishedBusinessId(int displayId) { + return "[" + displayId + "]unfinished business"; + } + + String getSuspendBlockerOnStateChangedId(int displayId) { + return "[" + displayId + "]on state changed"; + } + + String getSuspendBlockerProxPositiveId(int displayId) { + return "[" + displayId + "]prox positive"; + } + + String getSuspendBlockerProxNegativeId(int displayId) { + return "[" + displayId + "]prox negative"; + } + + @VisibleForTesting + String getSuspendBlockerProxDebounceId(int displayId) { + return "[" + displayId + "]prox debounce"; + } + + /** Functional interface for providing time. */ + @VisibleForTesting + interface Clock { + /** + * Returns current time in milliseconds since boot, not counting time spent in deep sleep. + */ + long uptimeMillis(); + } + + @VisibleForTesting + static class Injector { + Clock getClock() { + return SystemClock::uptimeMillis; + } + + DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, + int displayId, int displayState) { + return new DisplayPowerState(blanker, colorFade, displayId, displayState); + } + + DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps, + FloatProperty<DisplayPowerState> firstProperty, + FloatProperty<DisplayPowerState> secondProperty) { + return new DualRampAnimator(dps, firstProperty, secondProperty); + } + } + static class CachedBrightnessInfo { public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); public MutableFloat adjustedBrightness = - new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); + new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); public MutableFloat brightnessMin = - new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); + new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); public MutableFloat brightnessMax = - new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); + new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT); public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); public MutableFloat hbmTransitionPoint = - new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID); + new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID); public MutableInt brightnessMaxReason = - new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE); + new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE); public boolean checkAndSetFloat(MutableFloat mf, float f) { if (mf.value != f) { diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java new file mode 100644 index 000000000000..6a18dc181393 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -0,0 +1,236 @@ +/* + * 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.server.display; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + + +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isA; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.ContextWrapper; +import android.content.res.Resources; +import android.hardware.Sensor; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks; +import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; +import android.os.Handler; +import android.os.PowerManager; +import android.os.test.TestLooper; +import android.util.FloatProperty; +import android.view.Display; +import android.view.DisplayInfo; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.LocalServices; +import com.android.server.display.RampAnimator.DualRampAnimator; +import com.android.server.policy.WindowManagerPolicy; +import com.android.server.testutils.OffsettableClock; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + + +@SmallTest +@RunWith(AndroidJUnit4.class) +public final class DisplayPowerControllerTest { + private static final String UNIQUE_DISPLAY_ID = "unique_id_test123"; + private static final int DISPLAY_ID = 42; + + private OffsettableClock mClock; + private TestLooper mTestLooper; + private Handler mHandler; + private DisplayPowerController.Injector mInjector; + private Context mContextSpy; + + @Mock + private DisplayPowerCallbacks mDisplayPowerCallbacksMock; + @Mock + private SensorManager mSensorManagerMock; + @Mock + private DisplayBlanker mDisplayBlankerMock; + @Mock + private LogicalDisplay mLogicalDisplayMock; + @Mock + private DisplayDevice mDisplayDeviceMock; + @Mock + private BrightnessTracker mBrightnessTrackerMock; + @Mock + private BrightnessSetting mBrightnessSettingMock; + @Mock + private WindowManagerPolicy mWindowManagerPolicyMock; + @Mock + private PowerManager mPowerManagerMock; + @Mock + private Resources mResourcesMock; + @Mock + private DisplayDeviceConfig mDisplayDeviceConfigMock; + @Mock + private DisplayPowerState mDisplayPowerStateMock; + @Mock + private DualRampAnimator<DisplayPowerState> mDualRampAnimatorMock; + + @Captor + private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext())); + mClock = new OffsettableClock.Stopped(); + mTestLooper = new TestLooper(mClock::now); + mHandler = new Handler(mTestLooper.getLooper()); + mInjector = 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<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps, + FloatProperty<DisplayPowerState> firstProperty, + FloatProperty<DisplayPowerState> secondProperty) { + return mDualRampAnimatorMock; + } + }; + + addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock); + + when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock); + when(mContextSpy.getResources()).thenReturn(mResourcesMock); + } + + @After + public void tearDown() { + LocalServices.removeServiceForTest(WindowManagerPolicy.class); + } + + @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, () -> { + }); + + 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 */); + + // Run updatePowerState to start listener for the prox sensor + advanceTime(1); + + SensorEventListener listener = getSensorEventListener(proxSensor); + assertNotNull(listener); + + listener.onSensorChanged(TestUtils.createSensorEvent(proxSensor, 5 /* lux */)); + advanceTime(1); + + // two times, one for unfinished business and one for proximity + verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker( + dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID)); + verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker( + dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID)); + + dpc.stop(); + advanceTime(1); + + // two times, one for unfinished business and one for proximity + verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker( + dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID)); + verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker( + dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID)); + } + + /** + * Creates a mock and registers it to {@link LocalServices}. + */ + private static <T> void addLocalServiceMock(Class<T> clazz, T mock) { + LocalServices.removeServiceForTest(clazz); + LocalServices.addService(clazz, mock); + } + + private void advanceTime(long timeMs) { + mClock.fastForward(timeMs); + mTestLooper.dispatchAll(); + } + + private Sensor setUpProxSensor() throws Exception { + Sensor proxSensor = TestUtils.createSensor( + Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY); + when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL))) + .thenReturn(List.of(proxSensor)); + return proxSensor; + } + + private SensorEventListener getSensorEventListener(Sensor sensor) { + verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(), + eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class)); + return mSensorEventListenerCaptor.getValue(); + } + + private void setUpDisplay(int displayId, String uniqueId) { + 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.isEnabled()).thenReturn(true); + when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED); + when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo); + when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId); + when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock); + when(mDisplayDeviceConfigMock.getProximitySensor()).thenReturn( + new DisplayDeviceConfig.SensorData() { + { + type = Sensor.STRING_TYPE_PROXIMITY; + name = null; + } + }); + when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500}); + } +} diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 5d0554a1265d..af8c0891bff7 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -131,8 +131,9 @@ android_test { java_library { name: "servicestests-core-utils", srcs: [ - "src/com/android/server/pm/PackageSettingBuilder.java", "src/com/android/server/am/DeviceConfigSession.java", + "src/com/android/server/display/TestUtils.java", + "src/com/android/server/pm/PackageSettingBuilder.java", ], static_libs: [ "services.core", |