Merge "1) DM Generalization of Refresh Rates: adding calls to DM and Surface Control"
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6458737..1855a26 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -161,6 +161,8 @@
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);
@@ -1492,6 +1494,19 @@
/**
* @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 9f20388..bd202c1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -810,6 +810,16 @@
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;
@@ -1366,6 +1376,8 @@
(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 9882f6c..7ce63c5 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 @@
}
/**
- * 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 d24bd1a..6118df5 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 @@
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.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 @@
// 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 @@
*/
@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 @@
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;
+ 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 (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);
+ int defaultModeId = defaultMode.getModeId();
+ if (availableModes.length > 0) {
+ defaultModeId = availableModes[0];
}
- // If we haven't found anything with the current set of votes, drop the current lowest
- // priority vote.
- lowestConsideredPriority++;
+ // 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);
}
-
- // 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() };
}
private int[] filterModes(Display.Mode[] supportedModes,
@@ -403,6 +414,21 @@
}
}
+ @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 @@
}
}
- 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 @@
* The requested height of the display in pixels, or INVALID_SIZE;
*/
public final int height;
-
/**
- * The lowest desired refresh rate.
+ * Information about the min and max refresh rate DM would like to set the display to.
*/
- public final float minRefreshRate;
- /**
- * The highest desired refresh rate.
- */
- 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 @@
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 @@
@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 b03dc3b..1586473 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 @@
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 @@
}
@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 @@
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 @@
}
}
+ 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 @@
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 dcef998..f4b2dc8 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 @@
// 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 60cfbd0..739dd64 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 @@
}
@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 0000000..269f918
--- /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));
+ }
+ }
+}