Loading core/java/android/view/SurfaceControl.java +15 −0 Original line number Original line Diff line number Diff line Loading @@ -161,6 +161,8 @@ public final class SurfaceControl implements Parcelable { private static native boolean nativeSetAllowedDisplayConfigs(IBinder displayToken, private static native boolean nativeSetAllowedDisplayConfigs(IBinder displayToken, int[] allowedConfigs); int[] allowedConfigs); private static native int[] nativeGetAllowedDisplayConfigs(IBinder displayToken); 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 int[] nativeGetDisplayColorModes(IBinder displayToken); private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries( private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries( IBinder displayToken); IBinder displayToken); Loading Loading @@ -1489,6 +1491,19 @@ public final class SurfaceControl implements Parcelable { return nativeGetAllowedDisplayConfigs(displayToken); return nativeGetAllowedDisplayConfigs(displayToken); } } /** * @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 * @hide */ */ Loading core/jni/android_view_SurfaceControl.cpp +12 −0 Original line number Original line Diff line number Diff line Loading @@ -810,6 +810,16 @@ static jintArray nativeGetAllowedDisplayConfigs(JNIEnv* env, jclass clazz, jobje return allowedConfigsArray; 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) { static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == NULL) return -1; if (token == NULL) return -1; Loading Loading @@ -1366,6 +1376,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetAllowedDisplayConfigs }, (void*)nativeSetAllowedDisplayConfigs }, {"nativeGetAllowedDisplayConfigs", "(Landroid/os/IBinder;)[I", {"nativeGetAllowedDisplayConfigs", "(Landroid/os/IBinder;)[I", (void*)nativeGetAllowedDisplayConfigs }, (void*)nativeGetAllowedDisplayConfigs }, {"nativeSetDesiredDisplayConfigSpecs", "(Landroid/os/IBinder;IFF)Z", (void*)nativeSetDesiredDisplayConfigSpecs }, {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I", {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I", (void*)nativeGetDisplayColorModes}, (void*)nativeGetDisplayColorModes}, {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", Loading services/core/java/com/android/server/display/DisplayDevice.java +7 −3 Original line number Original line Diff line number Diff line Loading @@ -138,13 +138,17 @@ abstract class DisplayDevice { } } /** /** * Sets the display modes the system is allowed to switch between, roughly ordered by * Sets the refresh ranges, and display modes that the system is allowed to switch between. * preference. * Display modes are roughly ordered by preference. * * * Not all display devices will automatically switch between modes, so it's important that the * 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. * 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) { } } /** /** Loading services/core/java/com/android/server/display/DisplayModeDirector.java +232 −91 Original line number Original line Diff line number Diff line Loading @@ -18,26 +18,22 @@ package com.android.server.display; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.ContentResolver; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.content.res.Resources; import android.database.ContentObserver; import android.database.ContentObserver; import android.hardware.display.DisplayManager; import android.hardware.Sensor; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.SensorManager; import android.hardware.display.DisplayManager; import android.net.Uri; import android.net.Uri; import android.os.Handler; import android.os.Handler; import android.os.Looper; import android.os.Looper; import android.os.Message; import android.os.Message; import android.os.UserHandle; import android.os.PowerManager; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemClock; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils; Loading @@ -47,11 +43,11 @@ import android.util.SparseArray; import android.view.Display; import android.view.Display; import android.view.DisplayInfo; import android.view.DisplayInfo; import com.android.internal.os.BackgroundThread; import com.android.internal.R; 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.AmbientFilter; import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.ArrayList; Loading Loading @@ -87,11 +83,11 @@ public class DisplayModeDirector { // A map from the display ID to the collection of votes and their priority. The latter takes // 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 // 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. // 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. // 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. // 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 AppRequestObserver mAppRequestObserver; private final SettingsObserver mSettingsObserver; private final SettingsObserver mSettingsObserver; Loading Loading @@ -143,17 +139,7 @@ public class DisplayModeDirector { */ */ @NonNull @NonNull public int[] getAllowedModes(int displayId) { public int[] getAllowedModes(int displayId) { synchronized (mLock) { return getDesiredDisplayConfigSpecs(displayId).allowedConfigs; 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); } } } @NonNull @NonNull Loading @@ -178,26 +164,44 @@ public class DisplayModeDirector { return votes; 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 @NonNull private int[] getAllowedModesLocked(@NonNull SparseArray<Vote> votes, public DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs(int displayId) { @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) { synchronized (mLock) { int lowestConsideredPriority = Vote.MIN_PRIORITY; SparseArray<Vote> votes = getVotesLocked(displayId); while (lowestConsideredPriority <= Vote.MAX_PRIORITY) { 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 minRefreshRate = 0f; float maxRefreshRate = Float.POSITIVE_INFINITY; float maxRefreshRate = Float.POSITIVE_INFINITY; int lowestConsideredPriority = Vote.MIN_PRIORITY; while (lowestConsideredPriority <= Vote.MAX_PRIORITY) { int height = Vote.INVALID_SIZE; int height = Vote.INVALID_SIZE; int width = Vote.INVALID_SIZE; int width = Vote.INVALID_SIZE; for (int priority = Vote.MAX_PRIORITY; for (int priority = Vote.MAX_PRIORITY; priority >= lowestConsideredPriority; priority >= lowestConsideredPriority; priority--) { priority--) { Vote vote = votes.get(priority); Vote vote = votes.get(priority); if (vote == null) { if (vote == null) { continue; continue; } } // For refresh rates, just use the tightest bounds of all the votes // For refresh rates, just use the tightest bounds of all the votes minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate); minRefreshRate = Math.max(minRefreshRate, vote.refreshRateRange.min); maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate); maxRefreshRate = Math.min(maxRefreshRate, vote.refreshRateRange.max); // For display size, use only the first vote we come across (i.e. the highest // For display size, use only the first vote we come across (i.e. the highest // priority vote that includes the width / height). // priority vote that includes the width / height). if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE Loading @@ -207,16 +211,15 @@ public class DisplayModeDirector { } } } } // If we don't have anything specifying the width / height of the display, just use the // If we don't have anything specifying the width / height of the display, just use // default width and height. We don't want these switching out from underneath us since // the default width and height. We don't want these switching out from underneath // it's a pretty disruptive behavior. // us since it's a pretty disruptive behavior. if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) { if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) { width = defaultMode.getPhysicalWidth(); width = defaultMode.getPhysicalWidth(); height = defaultMode.getPhysicalHeight(); height = defaultMode.getPhysicalHeight(); } } int[] availableModes = availableModes = filterModes(modes, width, height, minRefreshRate, maxRefreshRate); filterModes(modes, width, height, minRefreshRate, maxRefreshRate); if (availableModes.length > 0) { if (availableModes.length > 0) { if (DEBUG) { if (DEBUG) { Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes) Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes) Loading @@ -228,7 +231,7 @@ public class DisplayModeDirector { + ", minRefreshRate=" + minRefreshRate + ", minRefreshRate=" + minRefreshRate + ", maxRefreshRate=" + maxRefreshRate); + ", maxRefreshRate=" + maxRefreshRate); } } return availableModes; break; } } if (DEBUG) { if (DEBUG) { Loading @@ -240,14 +243,22 @@ public class DisplayModeDirector { + ", minRefreshRate=" + minRefreshRate + ", minRefreshRate=" + minRefreshRate + ", maxRefreshRate=" + maxRefreshRate); + ", maxRefreshRate=" + maxRefreshRate); } } // If we haven't found anything with the current set of votes, drop the current lowest // priority vote. // If we haven't found anything with the current set of votes, drop the // current lowest priority vote. lowestConsideredPriority++; lowestConsideredPriority++; } } // If we still haven't found anything that matches our current set of votes, just fall back int defaultModeId = defaultMode.getModeId(); // to the default mode. if (availableModes.length > 0) { return new int[] { defaultMode.getModeId() }; 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, private int[] filterModes(Display.Mode[] supportedModes, Loading Loading @@ -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. * Listens for changes to display mode coordination. */ */ Loading Loading @@ -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. // 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. // 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. // It's used to avoid rate switch in certain conditions. Loading Loading @@ -499,15 +647,10 @@ public class DisplayModeDirector { * The requested height of the display in pixels, or INVALID_SIZE; * The requested height of the display in pixels, or INVALID_SIZE; */ */ public final int height; 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) { public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) { return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate); return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate); Loading @@ -521,8 +664,8 @@ public class DisplayModeDirector { float minRefreshRate, float maxRefreshRate) { float minRefreshRate, float maxRefreshRate) { this.width = width; this.width = width; this.height = height; this.height = height; this.minRefreshRate = minRefreshRate; this.refreshRateRange = this.maxRefreshRate = maxRefreshRate; new RefreshRateRange(minRefreshRate, maxRefreshRate); } } public static String priorityToString(int priority) { public static String priorityToString(int priority) { Loading @@ -547,11 +690,9 @@ public class DisplayModeDirector { @Override @Override public String toString() { public String toString() { return "Vote{" return "Vote{" + "width=" + width + "width=" + width + ", height=" + height + ", height=" + height + ", minRefreshRate=" + refreshRateRange.min + ", minRefreshRate=" + minRefreshRate + ", maxRefreshRate=" + refreshRateRange.max + "}"; + ", maxRefreshRate=" + maxRefreshRate + "}"; } } } } Loading services/core/java/com/android/server/display/LocalDisplayAdapter.java +40 −1 Original line number Original line Diff line number Diff line Loading @@ -173,6 +173,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { private int mActiveModeId; private int mActiveModeId; private boolean mActiveModeInvalid; private boolean mActiveModeInvalid; private int[] mAllowedModeIds; private int[] mAllowedModeIds; private float mMinRefreshRate; private float mMaxRefreshRate; private boolean mAllowedModeIdsInvalid; private boolean mAllowedModeIdsInvalid; private int mActivePhysIndex; private int mActivePhysIndex; private int[] mAllowedPhysIndexes; private int[] mAllowedPhysIndexes; Loading Loading @@ -623,7 +625,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } @Override @Override public void setAllowedDisplayModesLocked(int[] modes) { public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate, float maxRefreshRate, int[] modes) { updateDesiredDisplayConfigSpecs(defaultModeId, minRefreshRate, maxRefreshRate); updateAllowedModesLocked(modes); updateAllowedModesLocked(modes); } } Loading Loading @@ -653,6 +657,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; return true; } } // TODO(b/142507213): Remove once refresh rates are plummed through to kernel. public void updateAllowedModesLocked(int[] allowedModes) { public void updateAllowedModesLocked(int[] allowedModes) { if (Arrays.equals(allowedModes, mAllowedModeIds) && !mAllowedModeIdsInvalid) { if (Arrays.equals(allowedModes, mAllowedModeIds) && !mAllowedModeIdsInvalid) { return; return; Loading @@ -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) { public boolean updateAllowedModesInternalLocked(int[] allowedModes) { if (DEBUG) { if (DEBUG) { Slog.w(TAG, "updateAllowedModesInternalLocked(allowedModes=" Slog.w(TAG, "updateAllowedModesInternalLocked(allowedModes=" Loading Loading @@ -735,6 +772,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId); pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId); pw.println("mAllowedPhysIndexes=" + Arrays.toString(mAllowedPhysIndexes)); pw.println("mAllowedPhysIndexes=" + Arrays.toString(mAllowedPhysIndexes)); pw.println("mAllowedModeIds=" + Arrays.toString(mAllowedModeIds)); pw.println("mAllowedModeIds=" + Arrays.toString(mAllowedModeIds)); pw.println("mMinRefreshRate=" + mMinRefreshRate); pw.println("mMaxRefreshRate=" + mMaxRefreshRate); pw.println("mAllowedModeIdsInvalid=" + mAllowedModeIdsInvalid); pw.println("mAllowedModeIdsInvalid=" + mAllowedModeIdsInvalid); pw.println("mActivePhysIndex=" + mActivePhysIndex); pw.println("mActivePhysIndex=" + mActivePhysIndex); pw.println("mActiveModeId=" + mActiveModeId); pw.println("mActiveModeId=" + mActiveModeId); Loading Loading
core/java/android/view/SurfaceControl.java +15 −0 Original line number Original line Diff line number Diff line Loading @@ -161,6 +161,8 @@ public final class SurfaceControl implements Parcelable { private static native boolean nativeSetAllowedDisplayConfigs(IBinder displayToken, private static native boolean nativeSetAllowedDisplayConfigs(IBinder displayToken, int[] allowedConfigs); int[] allowedConfigs); private static native int[] nativeGetAllowedDisplayConfigs(IBinder displayToken); 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 int[] nativeGetDisplayColorModes(IBinder displayToken); private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries( private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries( IBinder displayToken); IBinder displayToken); Loading Loading @@ -1489,6 +1491,19 @@ public final class SurfaceControl implements Parcelable { return nativeGetAllowedDisplayConfigs(displayToken); return nativeGetAllowedDisplayConfigs(displayToken); } } /** * @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 * @hide */ */ Loading
core/jni/android_view_SurfaceControl.cpp +12 −0 Original line number Original line Diff line number Diff line Loading @@ -810,6 +810,16 @@ static jintArray nativeGetAllowedDisplayConfigs(JNIEnv* env, jclass clazz, jobje return allowedConfigsArray; 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) { static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == NULL) return -1; if (token == NULL) return -1; Loading Loading @@ -1366,6 +1376,8 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetAllowedDisplayConfigs }, (void*)nativeSetAllowedDisplayConfigs }, {"nativeGetAllowedDisplayConfigs", "(Landroid/os/IBinder;)[I", {"nativeGetAllowedDisplayConfigs", "(Landroid/os/IBinder;)[I", (void*)nativeGetAllowedDisplayConfigs }, (void*)nativeGetAllowedDisplayConfigs }, {"nativeSetDesiredDisplayConfigSpecs", "(Landroid/os/IBinder;IFF)Z", (void*)nativeSetDesiredDisplayConfigSpecs }, {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I", {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I", (void*)nativeGetDisplayColorModes}, (void*)nativeGetDisplayColorModes}, {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", Loading
services/core/java/com/android/server/display/DisplayDevice.java +7 −3 Original line number Original line Diff line number Diff line Loading @@ -138,13 +138,17 @@ abstract class DisplayDevice { } } /** /** * Sets the display modes the system is allowed to switch between, roughly ordered by * Sets the refresh ranges, and display modes that the system is allowed to switch between. * preference. * Display modes are roughly ordered by preference. * * * Not all display devices will automatically switch between modes, so it's important that the * 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. * 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) { } } /** /** Loading
services/core/java/com/android/server/display/DisplayModeDirector.java +232 −91 Original line number Original line Diff line number Diff line Loading @@ -18,26 +18,22 @@ package com.android.server.display; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.ContentResolver; import android.content.Context; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Resources; import android.content.res.Resources; import android.database.ContentObserver; import android.database.ContentObserver; import android.hardware.display.DisplayManager; import android.hardware.Sensor; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.SensorManager; import android.hardware.display.DisplayManager; import android.net.Uri; import android.net.Uri; import android.os.Handler; import android.os.Handler; import android.os.Looper; import android.os.Looper; import android.os.Message; import android.os.Message; import android.os.UserHandle; import android.os.PowerManager; import android.os.PowerManager; import android.os.SystemClock; import android.os.SystemClock; import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings; import android.text.TextUtils; import android.text.TextUtils; Loading @@ -47,11 +43,11 @@ import android.util.SparseArray; import android.view.Display; import android.view.Display; import android.view.DisplayInfo; import android.view.DisplayInfo; import com.android.internal.os.BackgroundThread; import com.android.internal.R; 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.AmbientFilter; import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.ArrayList; Loading Loading @@ -87,11 +83,11 @@ public class DisplayModeDirector { // A map from the display ID to the collection of votes and their priority. The latter takes // 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 // 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. // 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. // 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. // 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 AppRequestObserver mAppRequestObserver; private final SettingsObserver mSettingsObserver; private final SettingsObserver mSettingsObserver; Loading Loading @@ -143,17 +139,7 @@ public class DisplayModeDirector { */ */ @NonNull @NonNull public int[] getAllowedModes(int displayId) { public int[] getAllowedModes(int displayId) { synchronized (mLock) { return getDesiredDisplayConfigSpecs(displayId).allowedConfigs; 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); } } } @NonNull @NonNull Loading @@ -178,26 +164,44 @@ public class DisplayModeDirector { return votes; 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 @NonNull private int[] getAllowedModesLocked(@NonNull SparseArray<Vote> votes, public DesiredDisplayConfigSpecs getDesiredDisplayConfigSpecs(int displayId) { @NonNull Display.Mode[] modes, @NonNull Display.Mode defaultMode) { synchronized (mLock) { int lowestConsideredPriority = Vote.MIN_PRIORITY; SparseArray<Vote> votes = getVotesLocked(displayId); while (lowestConsideredPriority <= Vote.MAX_PRIORITY) { 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 minRefreshRate = 0f; float maxRefreshRate = Float.POSITIVE_INFINITY; float maxRefreshRate = Float.POSITIVE_INFINITY; int lowestConsideredPriority = Vote.MIN_PRIORITY; while (lowestConsideredPriority <= Vote.MAX_PRIORITY) { int height = Vote.INVALID_SIZE; int height = Vote.INVALID_SIZE; int width = Vote.INVALID_SIZE; int width = Vote.INVALID_SIZE; for (int priority = Vote.MAX_PRIORITY; for (int priority = Vote.MAX_PRIORITY; priority >= lowestConsideredPriority; priority >= lowestConsideredPriority; priority--) { priority--) { Vote vote = votes.get(priority); Vote vote = votes.get(priority); if (vote == null) { if (vote == null) { continue; continue; } } // For refresh rates, just use the tightest bounds of all the votes // For refresh rates, just use the tightest bounds of all the votes minRefreshRate = Math.max(minRefreshRate, vote.minRefreshRate); minRefreshRate = Math.max(minRefreshRate, vote.refreshRateRange.min); maxRefreshRate = Math.min(maxRefreshRate, vote.maxRefreshRate); maxRefreshRate = Math.min(maxRefreshRate, vote.refreshRateRange.max); // For display size, use only the first vote we come across (i.e. the highest // For display size, use only the first vote we come across (i.e. the highest // priority vote that includes the width / height). // priority vote that includes the width / height). if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE if (height == Vote.INVALID_SIZE && width == Vote.INVALID_SIZE Loading @@ -207,16 +211,15 @@ public class DisplayModeDirector { } } } } // If we don't have anything specifying the width / height of the display, just use the // If we don't have anything specifying the width / height of the display, just use // default width and height. We don't want these switching out from underneath us since // the default width and height. We don't want these switching out from underneath // it's a pretty disruptive behavior. // us since it's a pretty disruptive behavior. if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) { if (height == Vote.INVALID_SIZE || width == Vote.INVALID_SIZE) { width = defaultMode.getPhysicalWidth(); width = defaultMode.getPhysicalWidth(); height = defaultMode.getPhysicalHeight(); height = defaultMode.getPhysicalHeight(); } } int[] availableModes = availableModes = filterModes(modes, width, height, minRefreshRate, maxRefreshRate); filterModes(modes, width, height, minRefreshRate, maxRefreshRate); if (availableModes.length > 0) { if (availableModes.length > 0) { if (DEBUG) { if (DEBUG) { Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes) Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes) Loading @@ -228,7 +231,7 @@ public class DisplayModeDirector { + ", minRefreshRate=" + minRefreshRate + ", minRefreshRate=" + minRefreshRate + ", maxRefreshRate=" + maxRefreshRate); + ", maxRefreshRate=" + maxRefreshRate); } } return availableModes; break; } } if (DEBUG) { if (DEBUG) { Loading @@ -240,14 +243,22 @@ public class DisplayModeDirector { + ", minRefreshRate=" + minRefreshRate + ", minRefreshRate=" + minRefreshRate + ", maxRefreshRate=" + maxRefreshRate); + ", maxRefreshRate=" + maxRefreshRate); } } // If we haven't found anything with the current set of votes, drop the current lowest // priority vote. // If we haven't found anything with the current set of votes, drop the // current lowest priority vote. lowestConsideredPriority++; lowestConsideredPriority++; } } // If we still haven't found anything that matches our current set of votes, just fall back int defaultModeId = defaultMode.getModeId(); // to the default mode. if (availableModes.length > 0) { return new int[] { defaultMode.getModeId() }; 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, private int[] filterModes(Display.Mode[] supportedModes, Loading Loading @@ -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. * Listens for changes to display mode coordination. */ */ Loading Loading @@ -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. // 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. // 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. // It's used to avoid rate switch in certain conditions. Loading Loading @@ -499,15 +647,10 @@ public class DisplayModeDirector { * The requested height of the display in pixels, or INVALID_SIZE; * The requested height of the display in pixels, or INVALID_SIZE; */ */ public final int height; 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) { public static Vote forRefreshRates(float minRefreshRate, float maxRefreshRate) { return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate); return new Vote(INVALID_SIZE, INVALID_SIZE, minRefreshRate, maxRefreshRate); Loading @@ -521,8 +664,8 @@ public class DisplayModeDirector { float minRefreshRate, float maxRefreshRate) { float minRefreshRate, float maxRefreshRate) { this.width = width; this.width = width; this.height = height; this.height = height; this.minRefreshRate = minRefreshRate; this.refreshRateRange = this.maxRefreshRate = maxRefreshRate; new RefreshRateRange(minRefreshRate, maxRefreshRate); } } public static String priorityToString(int priority) { public static String priorityToString(int priority) { Loading @@ -547,11 +690,9 @@ public class DisplayModeDirector { @Override @Override public String toString() { public String toString() { return "Vote{" return "Vote{" + "width=" + width + "width=" + width + ", height=" + height + ", height=" + height + ", minRefreshRate=" + refreshRateRange.min + ", minRefreshRate=" + minRefreshRate + ", maxRefreshRate=" + refreshRateRange.max + "}"; + ", maxRefreshRate=" + maxRefreshRate + "}"; } } } } Loading
services/core/java/com/android/server/display/LocalDisplayAdapter.java +40 −1 Original line number Original line Diff line number Diff line Loading @@ -173,6 +173,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { private int mActiveModeId; private int mActiveModeId; private boolean mActiveModeInvalid; private boolean mActiveModeInvalid; private int[] mAllowedModeIds; private int[] mAllowedModeIds; private float mMinRefreshRate; private float mMaxRefreshRate; private boolean mAllowedModeIdsInvalid; private boolean mAllowedModeIdsInvalid; private int mActivePhysIndex; private int mActivePhysIndex; private int[] mAllowedPhysIndexes; private int[] mAllowedPhysIndexes; Loading Loading @@ -623,7 +625,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } @Override @Override public void setAllowedDisplayModesLocked(int[] modes) { public void setDesiredDisplayConfigSpecs(int defaultModeId, float minRefreshRate, float maxRefreshRate, int[] modes) { updateDesiredDisplayConfigSpecs(defaultModeId, minRefreshRate, maxRefreshRate); updateAllowedModesLocked(modes); updateAllowedModesLocked(modes); } } Loading Loading @@ -653,6 +657,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; return true; } } // TODO(b/142507213): Remove once refresh rates are plummed through to kernel. public void updateAllowedModesLocked(int[] allowedModes) { public void updateAllowedModesLocked(int[] allowedModes) { if (Arrays.equals(allowedModes, mAllowedModeIds) && !mAllowedModeIdsInvalid) { if (Arrays.equals(allowedModes, mAllowedModeIds) && !mAllowedModeIdsInvalid) { return; return; Loading @@ -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) { public boolean updateAllowedModesInternalLocked(int[] allowedModes) { if (DEBUG) { if (DEBUG) { Slog.w(TAG, "updateAllowedModesInternalLocked(allowedModes=" Slog.w(TAG, "updateAllowedModesInternalLocked(allowedModes=" Loading Loading @@ -735,6 +772,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId); pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId); pw.println("mAllowedPhysIndexes=" + Arrays.toString(mAllowedPhysIndexes)); pw.println("mAllowedPhysIndexes=" + Arrays.toString(mAllowedPhysIndexes)); pw.println("mAllowedModeIds=" + Arrays.toString(mAllowedModeIds)); pw.println("mAllowedModeIds=" + Arrays.toString(mAllowedModeIds)); pw.println("mMinRefreshRate=" + mMinRefreshRate); pw.println("mMaxRefreshRate=" + mMaxRefreshRate); pw.println("mAllowedModeIdsInvalid=" + mAllowedModeIdsInvalid); pw.println("mAllowedModeIdsInvalid=" + mAllowedModeIdsInvalid); pw.println("mActivePhysIndex=" + mActivePhysIndex); pw.println("mActivePhysIndex=" + mActivePhysIndex); pw.println("mActiveModeId=" + mActiveModeId); pw.println("mActiveModeId=" + mActiveModeId); Loading