diff options
| author | 2019-11-14 00:49:39 +0100 | |
|---|---|---|
| committer | 2019-11-25 14:00:53 -0800 | |
| commit | 4f753aacdf20a5edca2bc1d2f4da46e6dc361195 (patch) | |
| tree | 9eb4e79406d142e08387158f579d3063c69baac5 | |
| parent | a2186c9f79cb8e634ddbf067032f933bd9ea8769 (diff) | |
1) DM Generalization of Refresh Rates: adding calls to DM and Surface Control
Bug: 142507213
Test: 1. Manually verified refresh rate switching behavior on a phone
with refresh rate switching.
2. Wrote a new test file, DisplayModeDirectorTest.java, to verify the
behavior of the display mode voting algorithm.
Change-Id: Iceac6fd74e6528ddb2302f4d7eb926753e4ffd19
8 files changed, 449 insertions, 107 deletions
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 3251127397b6..dd59ce2fda81 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -161,6 +161,8 @@ public final class SurfaceControl implements Parcelable { private static native boolean nativeSetAllowedDisplayConfigs(IBinder displayToken, int[] allowedConfigs); private static native int[] nativeGetAllowedDisplayConfigs(IBinder displayToken); + private static native boolean nativeSetDesiredDisplayConfigSpecs(IBinder displayToken, + int defaultModeId, float minRefreshRate, float maxRefreshRate); private static native int[] nativeGetDisplayColorModes(IBinder displayToken); private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries( IBinder displayToken); @@ -1490,6 +1492,19 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ + public static boolean setDesiredDisplayConfigSpecs(IBinder displayToken, + int defaultModeId, float minRefreshRate, float maxRefreshRate) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + + return nativeSetDesiredDisplayConfigSpecs(displayToken, defaultModeId, minRefreshRate, + maxRefreshRate); + } + + /** + * @hide + */ public static int[] getDisplayColorModes(IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index f8a2744fdcb0..93530211b554 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -805,6 +805,16 @@ static jintArray nativeGetAllowedDisplayConfigs(JNIEnv* env, jclass clazz, jobje return allowedConfigsArray; } +static jboolean nativeSetDesiredDisplayConfigSpecs(JNIEnv* env, jclass clazz, + jobject tokenObj, jint displayModeId, jfloat minRefreshRate, jfloat maxRefreshRate) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == nullptr) return JNI_FALSE; + + size_t result = SurfaceComposerClient::setDesiredDisplayConfigSpecs( + token, displayModeId, minRefreshRate, maxRefreshRate); + return result == NO_ERROR ? JNI_TRUE : JNI_FALSE; +} + static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == NULL) return -1; @@ -1345,6 +1355,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetAllowedDisplayConfigs }, {"nativeGetAllowedDisplayConfigs", "(Landroid/os/IBinder;)[I", (void*)nativeGetAllowedDisplayConfigs }, + {"nativeSetDesiredDisplayConfigSpecs", "(Landroid/os/IBinder;IFF)Z", + (void*)nativeSetDesiredDisplayConfigSpecs }, {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I", (void*)nativeGetDisplayColorModes}, {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 9882f6c0d4c1..7ce63c5f89b2 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -138,13 +138,17 @@ abstract class DisplayDevice { } /** - * Sets the display modes the system is allowed to switch between, roughly ordered by - * preference. + * Sets the refresh ranges, and display modes that the system is allowed to switch between. + * Display modes are roughly ordered by preference. * * Not all display devices will automatically switch between modes, so it's important that the * most-desired modes are at the beginning of the allowed array. + * + * @param defaultModeId is used, if the device does not support multiple refresh + * rates, and to navigate other parameters. */ - public void setAllowedDisplayModesLocked(int[] modes) { + public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate, + float maxRefreshRate, int[] modes) { } /** diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index d24bd1aad1b1..6118df55e059 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -18,26 +18,22 @@ package com.android.server.display; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.res.Resources; import android.database.ContentObserver; -import android.hardware.display.DisplayManager; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; - +import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.os.UserHandle; import android.os.PowerManager; import android.os.SystemClock; +import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings; import android.text.TextUtils; @@ -47,11 +43,11 @@ import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; -import com.android.internal.os.BackgroundThread; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.BackgroundThread; import com.android.server.display.utils.AmbientFilter; import com.android.server.display.utils.AmbientFilterFactory; -import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory; import java.io.PrintWriter; import java.util.ArrayList; @@ -87,11 +83,11 @@ public class DisplayModeDirector { // A map from the display ID to the collection of votes and their priority. The latter takes // the form of another map from the priority to the vote itself so that each priority is // guaranteed to have exactly one vote, which is also easily and efficiently replaceable. - private final SparseArray<SparseArray<Vote>> mVotesByDisplay; + private SparseArray<SparseArray<Vote>> mVotesByDisplay; // A map from the display ID to the supported modes on that display. - private final SparseArray<Display.Mode[]> mSupportedModesByDisplay; + private SparseArray<Display.Mode[]> mSupportedModesByDisplay; // A map from the display ID to the default mode of that display. - private final SparseArray<Display.Mode> mDefaultModeByDisplay; + private SparseArray<Display.Mode> mDefaultModeByDisplay; private final AppRequestObserver mAppRequestObserver; private final SettingsObserver mSettingsObserver; @@ -143,17 +139,7 @@ public class DisplayModeDirector { */ @NonNull public int[] getAllowedModes(int displayId) { - synchronized (mLock) { - SparseArray<Vote> votes = getVotesLocked(displayId); - Display.Mode[] modes = mSupportedModesByDisplay.get(displayId); - Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId); - if (modes == null || defaultMode == null) { - Slog.e(TAG, "Asked about unknown display, returning empty allowed set! (id=" - + displayId + ")"); - return new int[0]; - } - return getAllowedModesLocked(votes, modes, defaultMode); - } + return getDesiredDisplayConfigSpecs(displayId).allowedConfigs; } @NonNull @@ -178,76 +164,101 @@ public class DisplayModeDirector { return votes; } + /** + * Calculates the refresh rate ranges and display modes that the system is allowed to freely + * switch between based on global and display-specific constraints. + * + * @param displayId The display to query for. + * @return The ID of the default mode the system should use, and the refresh rate range the + * system is allowed to switch between. + */ @NonNull - private int[] getAllowedModesLocked(@NonNull SparseArray<Vote> votes, - @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) { - int lowestConsideredPriority = Vote.MIN_PRIORITY; - while (lowestConsideredPriority <= Vote.MAX_PRIORITY) { + public DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs(int displayId) { + synchronized (mLock) { + SparseArray<Vote> votes = getVotesLocked(displayId); + Display.Mode[] modes = mSupportedModesByDisplay.get(displayId); + Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId); + if (modes == null || defaultMode == null) { + Slog.e(TAG, "Asked about unknown display, returning empty desired configs!" + + "(id=" + displayId + ")"); + return new DesiredDisplayConfigSpecs(displayId, new RefreshRateRange(60, 60), + new int[0]); + } + + int[] availableModes = new int[]{defaultMode.getModeId()}; float minRefreshRate = 0f; float maxRefreshRate = Float.POSITIVE_INFINITY; - int height = Vote.INVALID_SIZE; - int width = Vote.INVALID_SIZE; - - for (int priority = Vote.MAX_PRIORITY; - priority >= lowestConsideredPriority; - priority--) { - Vote vote = votes.get(priority); - if (vote == null) { - continue; + int lowestConsideredPriority = Vote.MIN_PRIORITY; + while (lowestConsideredPriority <= Vote.MAX_PRIORITY) { + int height = Vote.INVALID_SIZE; + int width = Vote.INVALID_SIZE; + + for (int priority = Vote.MAX_PRIORITY; + priority >= lowestConsideredPriority; priority--) { + Vote vote = votes.get(priority); + if (vote == null) { + continue; + } + // For refresh rates, just use the tightest bounds of all the votes + minRefreshRate = Math.max(minRefreshRate, vote.refreshRateRange.min); + maxRefreshRate = Math.min(maxRefreshRate, vote.refreshRateRange.max); + // For display size, use only the first vote we come across (i.e. the highest + // priority vote that includes the width / height). + if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE + && vote.height > 0 && vote.width > 0) { + width = vote.width; + height = vote.height; + } } - // For refresh rates, just use the tightest bounds of all the votes - minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate); - maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate); - // For display size, use only the first vote we come across (i.e. the highest - // priority vote that includes the width / height). - if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE - && vote.height > 0 && vote.width > 0) { - width = vote.width; - height = vote.height; + + // If we don't have anything specifying the width / height of the display, just use + // the default width and height. We don't want these switching out from underneath + // us since it's a pretty disruptive behavior. + if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) { + width = defaultMode.getPhysicalWidth(); + height = defaultMode.getPhysicalHeight(); } - } - // If we don't have anything specifying the width / height of the display, just use the - // default width and height. We don't want these switching out from underneath us since - // it's a pretty disruptive behavior. - if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) { - width = defaultMode.getPhysicalWidth(); - height = defaultMode.getPhysicalHeight(); - } + availableModes = filterModes(modes, width, height, minRefreshRate, maxRefreshRate); + if (availableModes.length > 0) { + if (DEBUG) { + Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes) + + " with lowest priority considered " + + Vote.priorityToString(lowestConsideredPriority) + + " and constraints: " + + "width=" + width + + ", height=" + height + + ", minRefreshRate=" + minRefreshRate + + ", maxRefreshRate=" + maxRefreshRate); + } + break; + } - int[] availableModes = - filterModes(modes, width, height, minRefreshRate, maxRefreshRate); - if (availableModes.length > 0) { if (DEBUG) { - Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes) - + " with lowest priority considered " + Slog.w(TAG, "Couldn't find available modes with lowest priority set to " + Vote.priorityToString(lowestConsideredPriority) - + " and constraints: " + + " and with the following constraints: " + "width=" + width + ", height=" + height + ", minRefreshRate=" + minRefreshRate + ", maxRefreshRate=" + maxRefreshRate); } - return availableModes; + + // If we haven't found anything with the current set of votes, drop the + // current lowest priority vote. + lowestConsideredPriority++; } - if (DEBUG) { - Slog.w(TAG, "Couldn't find available modes with lowest priority set to " - + Vote.priorityToString(lowestConsideredPriority) - + " and with the following constraints: " - + "width=" + width - + ", height=" + height - + ", minRefreshRate=" + minRefreshRate - + ", maxRefreshRate=" + maxRefreshRate); - } - // If we haven't found anything with the current set of votes, drop the current lowest - // priority vote. - lowestConsideredPriority++; - } - - // If we still haven't found anything that matches our current set of votes, just fall back - // to the default mode. - return new int[] { defaultMode.getModeId() }; + int defaultModeId = defaultMode.getModeId(); + if (availableModes.length > 0) { + defaultModeId = availableModes[0]; + } + // filterModes function is going to filter the modes based on the voting system. If + // the application requests a given mode with preferredModeId function, it will be + // stored as the first and only element in available modes array. + return new DesiredDisplayConfigSpecs(defaultModeId, + new RefreshRateRange(minRefreshRate, maxRefreshRate), availableModes); + } } private int[] filterModes(Display.Mode[] supportedModes, @@ -403,6 +414,21 @@ public class DisplayModeDirector { } } + @VisibleForTesting + void injectSupportedModesByDisplay(SparseArray<Display.Mode[]> supportedModesByDisplay) { + mSupportedModesByDisplay = supportedModesByDisplay; + } + + @VisibleForTesting + void injectDefaultModeByDisplay(SparseArray<Display.Mode> defaultModeByDisplay) { + mDefaultModeByDisplay = defaultModeByDisplay; + } + + @VisibleForTesting + void injectVotesByDisplay(SparseArray<SparseArray<Vote>> votesByDisplay) { + mVotesByDisplay = votesByDisplay; + } + /** * Listens for changes to display mode coordination. */ @@ -452,7 +478,129 @@ public class DisplayModeDirector { } } - private static final class Vote { + /** + * Information about the min and max refresh rate DM would like to set the display to. + */ + public static final class RefreshRateRange { + /** + * The lowest desired refresh rate. + */ + public final float min; + /** + * The highest desired refresh rate. + */ + public final float max; + + public RefreshRateRange(float min, float max) { + if (min < 0 || max < 0 || min > max) { + Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : " + + min + " " + max); + this.min = this.max = 0; + return; + } + this.min = min; + this.max = max; + } + + /** + * Checks whether the two objects have the same values. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof RefreshRateRange)) { + return false; + } + + RefreshRateRange refreshRateRange = (RefreshRateRange) other; + return (min == refreshRateRange.min && max == refreshRateRange.max); + } + + @Override + public int hashCode() { + return Objects.hash(min, max); + } + + @Override + public String toString() { + return "(" + min + " " + max + ")"; + } + } + + /** + * Information about the desired configuration to be set by the system. Includes the default + * configuration ID, refresh rate range, and the list of policy decisions that influenced the + * choice. + */ + public static final class DesiredDisplayConfigSpecs { + /** + * Default configuration ID. This is what system defaults to for all other settings, or + * if the refresh rate range is not available. + */ + public final int defaultModeId; + /** + * The refresh rate range. + */ + public final RefreshRateRange refreshRateRange; + /** + * For legacy reasons, keep a list of allowed configs. + * TODO(b/142507213): Re-assess whether the list of allowed configs is still necessary. + */ + public final int[] allowedConfigs; + + public DesiredDisplayConfigSpecs(int defaultModeId, + @NonNull RefreshRateRange refreshRateRange, + @NonNull int[] allowedConfigs) { + this.defaultModeId = defaultModeId; + this.refreshRateRange = refreshRateRange; + this.allowedConfigs = allowedConfigs; + } + + /** + * Returns a string representation of the object. + */ + @Override + public String toString() { + return "DesiredDisplayConfigSpecs(defaultModeId=" + defaultModeId + + ", refreshRateRange=" + refreshRateRange.toString() + + ", allowedConfigs=" + Arrays.toString(allowedConfigs) + ")"; + } + /** + * Checks whether the two objects have the same values. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof DesiredDisplayConfigSpecs)) { + return false; + } + + DesiredDisplayConfigSpecs desiredDisplayConfigSpecs = + (DesiredDisplayConfigSpecs) other; + + if (defaultModeId != desiredDisplayConfigSpecs.defaultModeId) { + return false; + } + if (!refreshRateRange.equals(desiredDisplayConfigSpecs.refreshRateRange)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return Objects.hash(defaultModeId, refreshRateRange); + } + } + + @VisibleForTesting + static final class Vote { // LOW_BRIGHTNESS votes for a single refresh rate like [60,60], [90,90] or null. // If the higher voters result is a range, it will fix the rate to a single choice. // It's used to avoid rate switch in certain conditions. @@ -499,15 +647,10 @@ public class DisplayModeDirector { * The requested height of the display in pixels, or INVALID_SIZE; */ public final int height; - /** - * The lowest desired refresh rate. - */ - public final float minRefreshRate; - /** - * The highest desired refresh rate. + * Information about the min and max refresh rate DM would like to set the display to. */ - public final float maxRefreshRate; + public final RefreshRateRange refreshRateRange; public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) { return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate); @@ -521,8 +664,8 @@ public class DisplayModeDirector { float minRefreshRate, float maxRefreshRate) { this.width = width; this.height = height; - this.minRefreshRate = minRefreshRate; - this.maxRefreshRate = maxRefreshRate; + this.refreshRateRange = + new RefreshRateRange(minRefreshRate, maxRefreshRate); } public static String priorityToString(int priority) { @@ -547,11 +690,9 @@ public class DisplayModeDirector { @Override public String toString() { return "Vote{" - + "width=" + width - + ", height=" + height - + ", minRefreshRate=" + minRefreshRate - + ", maxRefreshRate=" + maxRefreshRate - + "}"; + + "width=" + width + ", height=" + height + + ", minRefreshRate=" + refreshRateRange.min + + ", maxRefreshRate=" + refreshRateRange.max + "}"; } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index b03dc3b2ae93..158647395003 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -173,6 +173,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { private int mActiveModeId; private boolean mActiveModeInvalid; private int[] mAllowedModeIds; + private float mMinRefreshRate; + private float mMaxRefreshRate; private boolean mAllowedModeIdsInvalid; private int mActivePhysIndex; private int[] mAllowedPhysIndexes; @@ -623,7 +625,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override - public void setAllowedDisplayModesLocked(int[] modes) { + public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate, + float maxRefreshRate, int[] modes) { + updateDesiredDisplayConfigSpecs(defaultModeId, minRefreshRate, maxRefreshRate); updateAllowedModesLocked(modes); } @@ -653,6 +657,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + // TODO(b/142507213): Remove once refresh rates are plummed through to kernel. public void updateAllowedModesLocked(int[] allowedModes) { if (Arrays.equals(allowedModes, mAllowedModeIds) && !mAllowedModeIdsInvalid) { return; @@ -662,6 +667,38 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } + public void updateDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate, + float maxRefreshRate) { + if (minRefreshRate == mMinRefreshRate + && maxRefreshRate == mMaxRefreshRate + && defaultModeId == mDefaultModeId) { + return; + } + if (updateDesiredDisplayConfigSpecsInternalLocked(defaultModeId, minRefreshRate, + maxRefreshRate)) { + updateDeviceInfoLocked(); + } + } + + public boolean updateDesiredDisplayConfigSpecsInternalLocked(int defaultModeId, + float minRefreshRate, float maxRefreshRate) { + if (DEBUG) { + Slog.w(TAG, "updateDesiredDisplayConfigSpecsInternalLocked(" + + "defaultModeId=" + + Integer.toString(defaultModeId) + + ", minRefreshRate=" + + Float.toString(minRefreshRate) + + ", maxRefreshRate=" + + Float.toString(minRefreshRate)); + } + + final IBinder token = getDisplayTokenLocked(); + SurfaceControl.setDesiredDisplayConfigSpecs(token, defaultModeId, minRefreshRate, + maxRefreshRate); + int activePhysIndex = SurfaceControl.getActiveConfig(token); + return updateActiveModeLocked(activePhysIndex); + } + public boolean updateAllowedModesInternalLocked(int[] allowedModes) { if (DEBUG) { Slog.w(TAG, "updateAllowedModesInternalLocked(allowedModes=" @@ -735,6 +772,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId); pw.println("mAllowedPhysIndexes=" + Arrays.toString(mAllowedPhysIndexes)); pw.println("mAllowedModeIds=" + Arrays.toString(mAllowedModeIds)); + pw.println("mMinRefreshRate=" + mMinRefreshRate); + pw.println("mMaxRefreshRate=" + mMaxRefreshRate); pw.println("mAllowedModeIdsInvalid=" + mAllowedModeIdsInvalid); pw.println("mActivePhysIndex=" + mActivePhysIndex); pw.println("mActiveModeId=" + mActiveModeId); diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index dcef99817fac..f4b2dc8cfc98 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -352,11 +352,12 @@ final class LogicalDisplay { // Set the color mode and allowed display mode. if (device == mPrimaryDisplayDevice) { - device.setAllowedDisplayModesLocked(mAllowedDisplayModes); + // See ag/9588196 for correct values. + device.setDesiredDisplayConfigSpecs(0, 60, 60, mAllowedDisplayModes); device.setRequestedColorModeLocked(mRequestedColorMode); } else { // Reset to default for non primary displays - device.setAllowedDisplayModesLocked(new int[] {0}); + device.setDesiredDisplayConfigSpecs(0, 60, 60, new int[] {0}); device.setRequestedColorModeLocked(0); } diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index 60cfbd0da2c3..739dd64451e4 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -315,16 +315,9 @@ final class OverlayDisplayAdapter extends DisplayAdapter { } @Override - public void setAllowedDisplayModesLocked(int[] modes) { - final int id; - if (modes.length > 0) { - // The allowed modes should be ordered by preference, so just use the first mode - // here. - id = modes[0]; - } else { - // If we don't have any allowed modes, just use the default mode. - id = 0; - } + public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate, + float maxRefreshRate, int[] modes) { + final int id = defaultModeId; int index = -1; if (id == 0) { // Use the default. diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java new file mode 100644 index 000000000000..269f9180d8a6 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2019 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 org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.util.SparseArray; +import android.view.Display; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class DisplayModeDirectorTest { + private Context mContext; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + } + + private DisplayModeDirector createDisplayModeDirectorWithDisplayFpsRange( + int minFps, int maxFps) { + DisplayModeDirector director = + new DisplayModeDirector(mContext, new Handler(Looper.getMainLooper())); + int displayId = 0; + int numModes = maxFps - minFps + 1; + Display.Mode[] modes = new Display.Mode[numModes]; + for (int i = minFps; i <= maxFps; i++) { + modes[i - minFps] = new Display.Mode( + /*modeId=*/i, /*width=*/1000, /*height=*/1000, /*refreshRate=*/i); + } + SparseArray<Display.Mode[]> supportedModesByDisplay = new SparseArray<Display.Mode[]>(); + supportedModesByDisplay.put(displayId, modes); + director.injectSupportedModesByDisplay(supportedModesByDisplay); + SparseArray<Display.Mode> defaultModesByDisplay = new SparseArray<Display.Mode>(); + defaultModesByDisplay.put(displayId, modes[0]); + director.injectDefaultModeByDisplay(defaultModesByDisplay); + return director; + } + + private int[] intRange(int min, int max) { + int[] range = new int[max - min + 1]; + for (int i = min; i <= max; i++) { + range[i - min] = i; + } + return range; + } + + @Test + public void testDisplayModeVoting() { + int displayId = 0; + + // With no votes present, DisplayModeDirector should allow any refresh rate. + assertEquals(new DisplayModeDirector.DesiredDisplayConfigSpecs(/*defaultModeId=*/60, + new DisplayModeDirector.RefreshRateRange(0f, Float.POSITIVE_INFINITY), + intRange(60, 90)), + createDisplayModeDirectorWithDisplayFpsRange(60, 90).getDesiredDisplayConfigSpecs( + displayId)); + + int numPriorities = + DisplayModeDirector.Vote.MAX_PRIORITY - DisplayModeDirector.Vote.MIN_PRIORITY + 1; + + // Ensure vote priority works as expected. As we add new votes with higher priority, they + // should take precedence over lower priority votes. + { + int minFps = 60; + int maxFps = 90; + DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); + assertTrue(2 * numPriorities < maxFps - minFps + 1); + SparseArray<DisplayModeDirector.Vote> votes = + new SparseArray<DisplayModeDirector.Vote>(); + SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay = + new SparseArray<SparseArray<DisplayModeDirector.Vote>>(); + votesByDisplay.put(displayId, votes); + for (int i = 0; i < numPriorities; i++) { + int priority = DisplayModeDirector.Vote.MIN_PRIORITY + i; + votes.put( + priority, DisplayModeDirector.Vote.forRefreshRates(minFps + i, maxFps - i)); + director.injectVotesByDisplay(votesByDisplay); + assertEquals( + new DisplayModeDirector.DesiredDisplayConfigSpecs( + /*defaultModeId=*/minFps + i, + new DisplayModeDirector.RefreshRateRange(minFps + i, maxFps - i), + intRange(minFps + i, maxFps - i)), + director.getDesiredDisplayConfigSpecs(displayId)); + } + } + + // Ensure lower priority votes are able to influence the final decision, even in the + // presence of higher priority votes. + { + assertTrue(numPriorities >= 2); + DisplayModeDirector director = createDisplayModeDirectorWithDisplayFpsRange(60, 90); + SparseArray<DisplayModeDirector.Vote> votes = + new SparseArray<DisplayModeDirector.Vote>(); + SparseArray<SparseArray<DisplayModeDirector.Vote>> votesByDisplay = + new SparseArray<SparseArray<DisplayModeDirector.Vote>>(); + votesByDisplay.put(displayId, votes); + votes.put(DisplayModeDirector.Vote.MAX_PRIORITY, + DisplayModeDirector.Vote.forRefreshRates(65, 85)); + votes.put(DisplayModeDirector.Vote.MIN_PRIORITY, + DisplayModeDirector.Vote.forRefreshRates(70, 80)); + director.injectVotesByDisplay(votesByDisplay); + assertEquals( + new DisplayModeDirector.DesiredDisplayConfigSpecs(/*defaultModeId=*/70, + new DisplayModeDirector.RefreshRateRange(70, 80), intRange(70, 80)), + director.getDesiredDisplayConfigSpecs(displayId)); + } + } +} |