diff options
7 files changed, 342 insertions, 20 deletions
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 4f5b51d04c4b..cfdf8fab05c2 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -2099,6 +2099,65 @@ public final class SurfaceControl implements Parcelable { } } + /** + * Contains information of the idle time of the screen after which the refresh rate is to be + * reduced. + * + * @hide + */ + public static final class IdleScreenRefreshRateConfig { + /** + * The time(in ms) after which the refresh rate is to be reduced. Defaults to -1, which + * means no timeout has been configured for the current conditions + */ + public int timeoutMillis; + + public IdleScreenRefreshRateConfig() { + timeoutMillis = -1; + } + + public IdleScreenRefreshRateConfig(int timeoutMillis) { + this.timeoutMillis = timeoutMillis; + } + + /** + * Checks whether the two objects have the same values. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof IdleScreenRefreshRateConfig) || other == null) { + return false; + } + + IdleScreenRefreshRateConfig + idleScreenRefreshRateConfig = (IdleScreenRefreshRateConfig) other; + return timeoutMillis == idleScreenRefreshRateConfig.timeoutMillis; + } + + @Override + public int hashCode() { + return Objects.hash(timeoutMillis); + } + + @Override + public String toString() { + return "timeoutMillis: " + timeoutMillis; + } + + /** + * Copies the supplied object's values to this object. + */ + public void copyFrom(IdleScreenRefreshRateConfig other) { + if (other != null) { + this.timeoutMillis = other.timeoutMillis; + } + } + } + /** * Contains information about desired display configuration. @@ -2132,6 +2191,15 @@ public final class SurfaceControl implements Parcelable { */ public final RefreshRateRanges appRequestRanges; + /** + * Represents the idle time of the screen after which the associated display's refresh rate + * is to be reduced to preserve power + * Defaults to null, meaning that the device is not configured to have a timeout. + * Timeout value of -1 refers that the current conditions require no timeout + */ + @Nullable + public IdleScreenRefreshRateConfig idleScreenRefreshRateConfig; + public DesiredDisplayModeSpecs() { this.primaryRanges = new RefreshRateRanges(); this.appRequestRanges = new RefreshRateRanges(); @@ -2144,13 +2212,17 @@ public final class SurfaceControl implements Parcelable { } public DesiredDisplayModeSpecs(int defaultMode, boolean allowGroupSwitching, - RefreshRateRanges primaryRanges, RefreshRateRanges appRequestRanges) { + RefreshRateRanges primaryRanges, RefreshRateRanges appRequestRanges, + @Nullable IdleScreenRefreshRateConfig idleScreenRefreshRateConfig) { this.defaultMode = defaultMode; this.allowGroupSwitching = allowGroupSwitching; this.primaryRanges = new RefreshRateRanges(primaryRanges.physical, primaryRanges.render); this.appRequestRanges = new RefreshRateRanges(appRequestRanges.physical, appRequestRanges.render); + this.idleScreenRefreshRateConfig = + (idleScreenRefreshRateConfig == null) ? null : new IdleScreenRefreshRateConfig( + idleScreenRefreshRateConfig.timeoutMillis); } @Override @@ -2165,7 +2237,9 @@ public final class SurfaceControl implements Parcelable { return other != null && defaultMode == other.defaultMode && allowGroupSwitching == other.allowGroupSwitching && primaryRanges.equals(other.primaryRanges) - && appRequestRanges.equals(other.appRequestRanges); + && appRequestRanges.equals(other.appRequestRanges) + && Objects.equals( + idleScreenRefreshRateConfig, other.idleScreenRefreshRateConfig); } @Override @@ -2181,6 +2255,7 @@ public final class SurfaceControl implements Parcelable { allowGroupSwitching = other.allowGroupSwitching; primaryRanges.copyFrom(other.primaryRanges); appRequestRanges.copyFrom(other.appRequestRanges); + copyIdleScreenRefreshRateConfig(other.idleScreenRefreshRateConfig); } @Override @@ -2188,7 +2263,21 @@ public final class SurfaceControl implements Parcelable { return "defaultMode=" + defaultMode + " allowGroupSwitching=" + allowGroupSwitching + " primaryRanges=" + primaryRanges - + " appRequestRanges=" + appRequestRanges; + + " appRequestRanges=" + appRequestRanges + + " idleScreenRefreshRate=" + String.valueOf(idleScreenRefreshRateConfig); + } + + private void copyIdleScreenRefreshRateConfig(IdleScreenRefreshRateConfig other) { + if (idleScreenRefreshRateConfig == null) { + if (other != null) { + idleScreenRefreshRateConfig = + new IdleScreenRefreshRateConfig(other.timeoutMillis); + } + } else if (other == null) { + idleScreenRefreshRateConfig = null; + } else { + idleScreenRefreshRateConfig.copyFrom(other); + } } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 1eab9910b651..1aa635c6ceb7 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -208,10 +208,17 @@ static struct { static struct { jclass clazz; jmethodID ctor; + jfieldID timeoutMillis; +} gIdleScreenRefreshRateConfigClassInfo; + +static struct { + jclass clazz; + jmethodID ctor; jfieldID defaultMode; jfieldID allowGroupSwitching; jfieldID primaryRanges; jfieldID appRequestRanges; + jfieldID idleScreenRefreshRateConfig; } gDesiredDisplayModeSpecsClassInfo; static struct { @@ -1407,6 +1414,18 @@ static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobj return ranges; }; + const auto makeIdleScreenRefreshRateConfig = [env](jobject obj) + -> std::optional<gui::DisplayModeSpecs::IdleScreenRefreshRateConfig> { + if (obj == NULL) { + return std::nullopt; + } + gui::DisplayModeSpecs::IdleScreenRefreshRateConfig idleScreenRefreshRateConfig; + idleScreenRefreshRateConfig.timeoutMillis = + env->GetIntField(obj, gIdleScreenRefreshRateConfigClassInfo.timeoutMillis); + + return idleScreenRefreshRateConfig; + }; + gui::DisplayModeSpecs specs; specs.defaultMode = env->GetIntField(DesiredDisplayModeSpecs, gDesiredDisplayModeSpecsClassInfo.defaultMode); @@ -1421,6 +1440,10 @@ static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobj makeRanges(env->GetObjectField(DesiredDisplayModeSpecs, gDesiredDisplayModeSpecsClassInfo.appRequestRanges)); + specs.idleScreenRefreshRateConfig = makeIdleScreenRefreshRateConfig( + env->GetObjectField(DesiredDisplayModeSpecs, + gDesiredDisplayModeSpecsClassInfo.idleScreenRefreshRateConfig)); + size_t result = SurfaceComposerClient::setDesiredDisplayModeSpecs(token, specs); return result == NO_ERROR ? JNI_TRUE : JNI_FALSE; } @@ -1440,6 +1463,17 @@ static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobje rangeToJava(ranges.physical), rangeToJava(ranges.render)); }; + const auto idleScreenRefreshRateConfigToJava = + [env](const std::optional<gui::DisplayModeSpecs::IdleScreenRefreshRateConfig>& + idleScreenRefreshRateConfig) -> jobject { + if (!idleScreenRefreshRateConfig.has_value()) { + return NULL; // Return null if input config is null + } + return env->NewObject(gIdleScreenRefreshRateConfigClassInfo.clazz, + gIdleScreenRefreshRateConfigClassInfo.ctor, + idleScreenRefreshRateConfig->timeoutMillis); + }; + gui::DisplayModeSpecs specs; if (SurfaceComposerClient::getDesiredDisplayModeSpecs(token, &specs) != NO_ERROR) { return nullptr; @@ -1448,7 +1482,8 @@ static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobje return env->NewObject(gDesiredDisplayModeSpecsClassInfo.clazz, gDesiredDisplayModeSpecsClassInfo.ctor, specs.defaultMode, specs.allowGroupSwitching, rangesToJava(specs.primaryRanges), - rangesToJava(specs.appRequestRanges)); + rangesToJava(specs.appRequestRanges), + idleScreenRefreshRateConfigToJava(specs.idleScreenRefreshRateConfig)); } static jobject nativeGetDisplayNativePrimaries(JNIEnv* env, jclass, jobject tokenObj) { @@ -2607,13 +2642,23 @@ int register_android_view_SurfaceControl(JNIEnv* env) GetFieldIDOrDie(env, RefreshRateRangesClazz, "render", "Landroid/view/SurfaceControl$RefreshRateRange;"); + jclass IdleScreenRefreshRateConfigClazz = + FindClassOrDie(env, "android/view/SurfaceControl$IdleScreenRefreshRateConfig"); + gIdleScreenRefreshRateConfigClassInfo.clazz = + MakeGlobalRefOrDie(env, IdleScreenRefreshRateConfigClazz); + gIdleScreenRefreshRateConfigClassInfo.ctor = + GetMethodIDOrDie(env, gIdleScreenRefreshRateConfigClassInfo.clazz, "<init>", "(I)V"); + gIdleScreenRefreshRateConfigClassInfo.timeoutMillis = + GetFieldIDOrDie(env, gIdleScreenRefreshRateConfigClassInfo.clazz, "timeoutMillis", "I"); + jclass DesiredDisplayModeSpecsClazz = FindClassOrDie(env, "android/view/SurfaceControl$DesiredDisplayModeSpecs"); gDesiredDisplayModeSpecsClassInfo.clazz = MakeGlobalRefOrDie(env, DesiredDisplayModeSpecsClazz); gDesiredDisplayModeSpecsClassInfo.ctor = GetMethodIDOrDie(env, gDesiredDisplayModeSpecsClassInfo.clazz, "<init>", "(IZLandroid/view/SurfaceControl$RefreshRateRanges;Landroid/view/" - "SurfaceControl$RefreshRateRanges;)V"); + "SurfaceControl$RefreshRateRanges;Landroid/view/" + "SurfaceControl$IdleScreenRefreshRateConfig;)V"); gDesiredDisplayModeSpecsClassInfo.defaultMode = GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "defaultMode", "I"); gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching = @@ -2624,6 +2669,9 @@ int register_android_view_SurfaceControl(JNIEnv* env) gDesiredDisplayModeSpecsClassInfo.appRequestRanges = GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRanges", "Landroid/view/SurfaceControl$RefreshRateRanges;"); + gDesiredDisplayModeSpecsClassInfo.idleScreenRefreshRateConfig = + GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "idleScreenRefreshRateConfig", + "Landroid/view/SurfaceControl$IdleScreenRefreshRateConfig;"); jclass jankDataClazz = FindClassOrDie(env, "android/view/SurfaceControl$JankData"); diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 411666942b6d..831dcf476a49 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -3376,6 +3376,12 @@ public class DisplayDeviceConfig { throw new RuntimeException("Lux values should be in ascending order in the" + " idle screen refresh rate timeout config"); } + + int timeout = point.getTimeout().intValue(); + if (timeout < 0) { + throw new RuntimeException("The timeout value cannot be negative in" + + " idle screen refresh rate timeout config"); + } previousLux = newLux; } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index b2fd9edf61fe..c84c9b1f306e 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -1073,7 +1073,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { new SurfaceControl.DesiredDisplayModeSpecs(baseSfModeId, mDisplayModeSpecs.allowGroupSwitching, mDisplayModeSpecs.primary, - mDisplayModeSpecs.appRequest))); + mDisplayModeSpecs.appRequest, + mDisplayModeSpecs.mIdleScreenRefreshRateConfig))); } } diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 495ae87fe0b9..572d32e80c12 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -64,6 +64,8 @@ import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.view.Display; import android.view.DisplayInfo; +import android.view.SurfaceControl; +import android.view.SurfaceControl.IdleScreenRefreshRateConfig; import android.view.SurfaceControl.RefreshRateRange; import android.view.SurfaceControl.RefreshRateRanges; @@ -74,6 +76,7 @@ import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.display.DisplayDeviceConfig; +import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint; import com.android.server.display.feature.DeviceConfigParameterProvider; import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.utils.AmbientFilter; @@ -184,6 +187,8 @@ public class DisplayModeDirector { private final boolean mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled; + private final DisplayManagerFlags mDisplayManagerFlags; + private final boolean mDvrrSupported; @@ -206,7 +211,7 @@ public class DisplayModeDirector { .isDisplaysRefreshRatesSynchronizationEnabled(); mIsBackUpSmoothDisplayAndForcePeakRefreshRateEnabled = displayManagerFlags .isBackUpSmoothDisplayAndForcePeakRefreshRateEnabled(); - + mDisplayManagerFlags = displayManagerFlags; mContext = context; mHandler = new DisplayModeDirectorHandler(handler.getLooper()); mInjector = injector; @@ -374,7 +379,7 @@ public class DisplayModeDirector { final RefreshRateRanges ranges = new RefreshRateRanges(range, range); return new DesiredDisplayModeSpecs(defaultMode.getModeId(), /*allowGroupSwitching */ false, - ranges, ranges); + ranges, ranges, mBrightnessObserver.getIdleScreenRefreshRateConfig()); } boolean modeSwitchingDisabled = @@ -422,7 +427,8 @@ public class DisplayModeDirector { appRequestSummary.maxPhysicalRefreshRate), new RefreshRateRange( appRequestSummary.minRenderFrameRate, - appRequestSummary.maxRenderFrameRate))); + appRequestSummary.maxRenderFrameRate)), + mBrightnessObserver.getIdleScreenRefreshRateConfig()); } } @@ -764,6 +770,16 @@ public class DisplayModeDirector { public boolean allowGroupSwitching; /** + * Represents the idle time of the screen after which the associated display's refresh rate + * is to be reduced to preserve power + * Defaults to null, meaning that the device is not configured to have a timeout based on + * the surrounding conditions + * -1 means that the current conditions require no timeout + */ + @Nullable + public IdleScreenRefreshRateConfig mIdleScreenRefreshRateConfig; + + /** * The primary refresh rate ranges. */ public final RefreshRateRanges primary; @@ -783,11 +799,13 @@ public class DisplayModeDirector { public DesiredDisplayModeSpecs(int baseModeId, boolean allowGroupSwitching, @NonNull RefreshRateRanges primary, - @NonNull RefreshRateRanges appRequest) { + @NonNull RefreshRateRanges appRequest, + @Nullable SurfaceControl.IdleScreenRefreshRateConfig idleScreenRefreshRateConfig) { this.baseModeId = baseModeId; this.allowGroupSwitching = allowGroupSwitching; this.primary = primary; this.appRequest = appRequest; + this.mIdleScreenRefreshRateConfig = idleScreenRefreshRateConfig; } /** @@ -797,9 +815,10 @@ public class DisplayModeDirector { public String toString() { return String.format("baseModeId=%d allowGroupSwitching=%b" + " primary=%s" - + " appRequest=%s", + + " appRequest=%s" + + " idleScreenRefreshRateConfig=%s", baseModeId, allowGroupSwitching, primary.toString(), - appRequest.toString()); + appRequest.toString(), String.valueOf(mIdleScreenRefreshRateConfig)); } /** @@ -830,12 +849,18 @@ public class DisplayModeDirector { desiredDisplayModeSpecs.appRequest)) { return false; } + + if (!Objects.equals(mIdleScreenRefreshRateConfig, + desiredDisplayModeSpecs.mIdleScreenRefreshRateConfig)) { + return false; + } return true; } @Override public int hashCode() { - return Objects.hash(baseModeId, allowGroupSwitching, primary, appRequest); + return Objects.hash(baseModeId, allowGroupSwitching, primary, appRequest, + mIdleScreenRefreshRateConfig); } /** @@ -853,6 +878,14 @@ public class DisplayModeDirector { appRequest.physical.max = other.appRequest.physical.max; appRequest.render.min = other.appRequest.render.min; appRequest.render.max = other.appRequest.render.max; + + if (other.mIdleScreenRefreshRateConfig == null) { + mIdleScreenRefreshRateConfig = null; + } else { + mIdleScreenRefreshRateConfig = + new IdleScreenRefreshRateConfig( + other.mIdleScreenRefreshRateConfig.timeoutMillis); + } } } @@ -1543,12 +1576,20 @@ public class DisplayModeDirector { private float mAmbientLux = -1.0f; private AmbientFilter mAmbientFilter; + /** + * The current timeout configuration. This value is used by surface flinger to track the + * time after which an idle screen's refresh rate is to be reduced. + */ + @Nullable + private SurfaceControl.IdleScreenRefreshRateConfig mIdleScreenRefreshRateConfig; + private float mBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT; private final Context mContext; private final Injector mInjector; private final Handler mHandler; + private final boolean mVsyncLowLightBlockingVoteEnabled; private final IThermalEventListener.Stub mThermalListener = @@ -1643,6 +1684,11 @@ public class DisplayModeDirector { return mRefreshRateInLowZone; } + @VisibleForTesting + IdleScreenRefreshRateConfig getIdleScreenRefreshRateConfig() { + return mIdleScreenRefreshRateConfig; + } + private void loadLowBrightnessThresholds(@Nullable DisplayDeviceConfig displayDeviceConfig, boolean attemptReadFromFeatureParams) { loadRefreshRateInHighZone(displayDeviceConfig, attemptReadFromFeatureParams); @@ -2381,6 +2427,10 @@ public class DisplayModeDirector { // is interrupted by a new sensor event. mHandler.postDelayed(mInjectSensorEventRunnable, INJECT_EVENTS_INTERVAL_MS); } + + if (mDisplayManagerFlags.isIdleScreenRefreshRateTimeoutEnabled()) { + updateIdleScreenRefreshRate(mAmbientLux); + } } @Override @@ -2440,6 +2490,40 @@ public class DisplayModeDirector { } }; } + + private void updateIdleScreenRefreshRate(float ambientLux) { + List<IdleScreenRefreshRateTimeoutLuxThresholdPoint> + idleScreenRefreshRateTimeoutLuxThresholdPoints; + synchronized (mLock) { + if (mDefaultDisplayDeviceConfig == null || mDefaultDisplayDeviceConfig + .getIdleScreenRefreshRateTimeoutLuxThresholdPoint().isEmpty()) { + // Setting this to null will let surface flinger know that the idle timer is not + // configured in the display configs + mIdleScreenRefreshRateConfig = null; + return; + } + + idleScreenRefreshRateTimeoutLuxThresholdPoints = + mDefaultDisplayDeviceConfig + .getIdleScreenRefreshRateTimeoutLuxThresholdPoint(); + } + int newTimeout = -1; + for (IdleScreenRefreshRateTimeoutLuxThresholdPoint point : + idleScreenRefreshRateTimeoutLuxThresholdPoints) { + int newLux = point.getLux().intValue(); + if (newLux <= ambientLux) { + newTimeout = point.getTimeout().intValue(); + } + } + if (mIdleScreenRefreshRateConfig == null + || newTimeout != mIdleScreenRefreshRateConfig.timeoutMillis) { + mIdleScreenRefreshRateConfig = + new IdleScreenRefreshRateConfig(newTimeout); + synchronized (mLock) { + notifyDesiredDisplayModeSpecsChangedLocked(); + } + } + } } private class UdfpsObserver extends IUdfpsRefreshRateRequestCallback.Stub { diff --git a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java index 14de527aa1f7..7fd96c57c215 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -49,6 +49,7 @@ import android.os.Looper; import android.view.Display; import android.view.DisplayAddress; import android.view.SurfaceControl; +import android.view.SurfaceControl.IdleScreenRefreshRateConfig; import android.view.SurfaceControl.RefreshRateRange; import android.view.SurfaceControl.RefreshRateRanges; @@ -830,18 +831,20 @@ public class LocalDisplayAdapterTest { .get() .getModeId(); + IdleScreenRefreshRateConfig + idleScreenRefreshRateConfig = new SurfaceControl.IdleScreenRefreshRateConfig(500); displayDevice.setDesiredDisplayModeSpecsLocked( new DisplayModeDirector.DesiredDisplayModeSpecs( /*baseModeId*/ baseModeId, /*allowGroupSwitching*/ false, - REFRESH_RATE_RANGES, REFRESH_RATE_RANGES + REFRESH_RATE_RANGES, REFRESH_RATE_RANGES, idleScreenRefreshRateConfig )); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token, new SurfaceControl.DesiredDisplayModeSpecs( /* baseModeId */ 0, /* allowGroupSwitching */ false, - REFRESH_RATE_RANGES, REFRESH_RATE_RANGES + REFRESH_RATE_RANGES, REFRESH_RATE_RANGES, idleScreenRefreshRateConfig )); // Change the display @@ -862,12 +865,13 @@ public class LocalDisplayAdapterTest { baseModeId = displayDevice.getDisplayDeviceInfoLocked().supportedModes[0].getModeId(); + idleScreenRefreshRateConfig = new SurfaceControl.IdleScreenRefreshRateConfig(600); // The traversal request will call setDesiredDisplayModeSpecsLocked on the display device displayDevice.setDesiredDisplayModeSpecsLocked( new DisplayModeDirector.DesiredDisplayModeSpecs( /*baseModeId*/ baseModeId, /*allowGroupSwitching*/ false, - REFRESH_RATE_RANGES, REFRESH_RATE_RANGES + REFRESH_RATE_RANGES, REFRESH_RATE_RANGES, idleScreenRefreshRateConfig )); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); @@ -877,7 +881,7 @@ public class LocalDisplayAdapterTest { new SurfaceControl.DesiredDisplayModeSpecs( /* baseModeId */ 2, /* allowGroupSwitching */ false, - REFRESH_RATE_RANGES, REFRESH_RATE_RANGES + REFRESH_RATE_RANGES, REFRESH_RATE_RANGES, idleScreenRefreshRateConfig )); } @@ -1319,7 +1323,8 @@ public class LocalDisplayAdapterTest { new SurfaceControl.DesiredDisplayModeSpecs( /* defaultMode */ 0, /* allowGroupSwitching */ false, - REFRESH_RATE_RANGES, REFRESH_RATE_RANGES + REFRESH_RATE_RANGES, REFRESH_RATE_RANGES, + new IdleScreenRefreshRateConfig(100) ); private FakeDisplay(int port) { diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index 3eced7fa025c..3a59c84b636c 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -75,6 +75,8 @@ import android.util.SparseArray; import android.util.TypedValue; import android.view.Display; import android.view.DisplayInfo; +import android.view.SurfaceControl; +import android.view.SurfaceControl.IdleScreenRefreshRateConfig; import android.view.SurfaceControl.RefreshRateRange; import android.view.SurfaceControl.RefreshRateRanges; @@ -91,6 +93,7 @@ import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.modules.utils.testing.ExtendedMockitoRule; import com.android.server.display.DisplayDeviceConfig; import com.android.server.display.TestUtils; +import com.android.server.display.config.IdleScreenRefreshRateTimeoutLuxThresholdPoint; import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.display.mode.DisplayModeDirector.BrightnessObserver; import com.android.server.display.mode.DisplayModeDirector.DesiredDisplayModeSpecs; @@ -112,6 +115,7 @@ import org.mockito.Mockito; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -1405,6 +1409,81 @@ public class DisplayModeDirectorTest { } @Test + public void testIdleScreenTimeOnLuxChanges() throws Exception { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f, 120.f}, 0); + setPeakRefreshRate(120 /*fps*/); + director.getSettingsObserver().setDefaultRefreshRate(120); + director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON); + + // Set the DisplayDeviceConfig + DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class); + when(ddcMock.getDefaultHighBlockingZoneRefreshRate()).thenReturn(90); + when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new float[] { 200 }); + when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new float[] { 8000 }); + when(ddcMock.getDefaultLowBlockingZoneRefreshRate()).thenReturn(90); + when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new float[] {}); + when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new float[] {}); + + director.defaultDisplayDeviceUpdated(ddcMock); // set the ddc + + Sensor lightSensor = createLightSensor(); + SensorManager sensorManager = createMockSensorManager(lightSensor); + director.start(sensorManager); + + // Get the sensor listener so that we can give it new light sensor events + ArgumentCaptor<SensorEventListener> listenerCaptor = + ArgumentCaptor.forClass(SensorEventListener.class); + verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1))) + .registerListener( + listenerCaptor.capture(), + eq(lightSensor), + anyInt(), + any(Handler.class)); + SensorEventListener sensorListener = listenerCaptor.getValue(); + + // Disable the idle screen flag + when(mDisplayManagerFlags.isIdleScreenRefreshRateTimeoutEnabled()) + .thenReturn(false); + + // Sensor reads 5 lux, with idleScreenRefreshRate timeout not configured + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 5)); + waitForIdleSync(); + assertEquals(null, director.getBrightnessObserver().getIdleScreenRefreshRateConfig()); + + // Enable the idle screen flag + when(mDisplayManagerFlags.isIdleScreenRefreshRateTimeoutEnabled()) + .thenReturn(true); + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 8)); + waitForIdleSync(); + assertEquals(null, director.getBrightnessObserver().getIdleScreenRefreshRateConfig()); + + // Configure DDC with idle screen timeout + when(ddcMock.getIdleScreenRefreshRateTimeoutLuxThresholdPoint()) + .thenReturn(List.of(getIdleScreenRefreshRateTimeoutLuxThresholdPoint(6, 1000), + getIdleScreenRefreshRateTimeoutLuxThresholdPoint(100, 800))); + + // Sensor reads 5 lux + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 5)); + waitForIdleSync(); + assertEquals(new SurfaceControl.IdleScreenRefreshRateConfig(-1), + director.getBrightnessObserver().getIdleScreenRefreshRateConfig()); + + // Sensor reads 50 lux + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 50)); + waitForIdleSync(); + assertEquals(new IdleScreenRefreshRateConfig(1000), + director.getBrightnessObserver().getIdleScreenRefreshRateConfig()); + + // Sensor reads 200 lux + sensorListener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, 200)); + waitForIdleSync(); + assertEquals(new SurfaceControl.IdleScreenRefreshRateConfig(800), + director.getBrightnessObserver().getIdleScreenRefreshRateConfig()); + + } + + @Test public void testLockFpsForHighZoneWithThermalCondition() throws Exception { // First, configure brightness zones or DMD won't register for sensor data. final FakeDeviceConfig config = mInjector.getDeviceConfig(); @@ -1440,11 +1519,11 @@ public class DisplayModeDirectorTest { // Get the display listener so that we can send it new brightness events ArgumentCaptor<DisplayListener> displayListenerCaptor = - ArgumentCaptor.forClass(DisplayListener.class); + ArgumentCaptor.forClass(DisplayListener.class); verify(mInjector).registerDisplayListener(displayListenerCaptor.capture(), any(Handler.class), eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED - | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS)); + | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS)); DisplayListener displayListener = displayListenerCaptor.getValue(); // Get the sensor listener so that we can give it new light sensor events @@ -3746,4 +3825,14 @@ public class DisplayModeDirectorTest { } } } + + private IdleScreenRefreshRateTimeoutLuxThresholdPoint + getIdleScreenRefreshRateTimeoutLuxThresholdPoint(int lux, int timeout) { + IdleScreenRefreshRateTimeoutLuxThresholdPoint + idleScreenRefreshRateTimeoutLuxThresholdPoint = + new IdleScreenRefreshRateTimeoutLuxThresholdPoint(); + idleScreenRefreshRateTimeoutLuxThresholdPoint.setLux(BigInteger.valueOf(lux)); + idleScreenRefreshRateTimeoutLuxThresholdPoint.setTimeout(BigInteger.valueOf(timeout)); + return idleScreenRefreshRateTimeoutLuxThresholdPoint; + } } |