diff options
11 files changed, 202 insertions, 22 deletions
diff --git a/api/current.txt b/api/current.txt index 7438d18..ea69ebc 100644 --- a/api/current.txt +++ b/api/current.txt @@ -33013,6 +33013,7 @@ package android.view { method public int getRotation(); method public void getSize(android.graphics.Point); method public int getState(); + method public float[] getSupportedRefreshRates(); method public deprecated int getWidth(); method public boolean isValid(); field public static final int DEFAULT_DISPLAY = 0; // 0x0 @@ -35656,6 +35657,7 @@ package android.view { field public float horizontalWeight; field public deprecated int memoryType; field public java.lang.String packageName; + field public float preferredRefreshRate; field public int rotationAnimation; field public float screenBrightness; field public int screenOrientation; diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index 99af2e7..8447dde 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -106,21 +106,30 @@ public abstract class DisplayManagerInternal { public abstract void performTraversalInTransactionFromWindowManager(); /** - * Tells the display manager whether there is interesting unique content on the - * specified logical display. This is used to control automatic mirroring. + * Tells the display manager about properties of the display that depend on the windows on it. + * This includes whether there is interesting unique content on the specified logical display, + * and whether the one of the windows has a preferred refresh rate. * <p> * If the display has unique content, then the display manager arranges for it * to be presented on a physical display if appropriate. Otherwise, the display manager * may choose to make the physical display mirror some other logical display. * </p> * + * <p> + * If one of the windows on the display has a preferred refresh rate that's supported by the + * display, then the display manager will request its use. + * </p> + * * @param displayId The logical display id to update. - * @param hasContent True if the logical display has content. + * @param hasContent True if the logical display has content. This is used to control automatic + * mirroring. + * @param requestedRefreshRate The preferred refresh rate for the top-most visible window that + * has a preference. * @param inTraversal True if called from WindowManagerService during a window traversal * prior to call to performTraversalInTransactionFromWindowManager. */ - public abstract void setDisplayHasContent(int displayId, boolean hasContent, - boolean inTraversal); + public abstract void setDisplayProperties(int displayId, boolean hasContent, + float requestedRefreshRate, boolean inTraversal); /** * Describes the requested power state of the display. diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 154d227..cfb0297 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -26,6 +26,8 @@ import android.os.SystemClock; import android.util.DisplayMetrics; import android.util.Log; +import java.util.Arrays; + /** * Provides information about the size and density of a logical display. * <p> @@ -613,6 +615,17 @@ public final class Display { } /** + * Get the supported refresh rates of this display in frames per second. + */ + public float[] getSupportedRefreshRates() { + synchronized (this) { + updateDisplayInfoLocked(); + final float[] refreshRates = mDisplayInfo.supportedRefreshRates; + return Arrays.copyOf(refreshRates, refreshRates.length); + } + } + + /** * Gets the app VSYNC offset, in nanoseconds. This is a positive value indicating * the phase offset of the VSYNC events provided by Choreographer relative to the * display refresh. For example, if Choreographer reports that the refresh occurred diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index 98696c7..56a05fe 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -22,6 +22,9 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.DisplayMetrics; +import java.util.Arrays; + +import libcore.util.EmptyArray; import libcore.util.Objects; /** @@ -156,6 +159,11 @@ public final class DisplayInfo implements Parcelable { public float refreshRate; /** + * The supported refresh rates of this display at the current resolution in frames per second. + */ + public float[] supportedRefreshRates = EmptyArray.FLOAT; + + /** * The logical display density which is the basis for density-independent * pixels. */ @@ -299,6 +307,8 @@ public final class DisplayInfo implements Parcelable { overscanBottom = other.overscanBottom; rotation = other.rotation; refreshRate = other.refreshRate; + supportedRefreshRates = Arrays.copyOf( + other.supportedRefreshRates, other.supportedRefreshRates.length); logicalDensityDpi = other.logicalDensityDpi; physicalXDpi = other.physicalXDpi; physicalYDpi = other.physicalYDpi; @@ -329,6 +339,7 @@ public final class DisplayInfo implements Parcelable { overscanBottom = source.readInt(); rotation = source.readInt(); refreshRate = source.readFloat(); + supportedRefreshRates = source.createFloatArray(); logicalDensityDpi = source.readInt(); physicalXDpi = source.readFloat(); physicalYDpi = source.readFloat(); @@ -360,6 +371,7 @@ public final class DisplayInfo implements Parcelable { dest.writeInt(overscanBottom); dest.writeInt(rotation); dest.writeFloat(refreshRate); + dest.writeFloatArray(supportedRefreshRates); dest.writeInt(logicalDensityDpi); dest.writeFloat(physicalXDpi); dest.writeFloat(physicalYDpi); @@ -462,7 +474,9 @@ public final class DisplayInfo implements Parcelable { sb.append(smallestNominalAppHeight); sb.append(", "); sb.append(refreshRate); - sb.append(" fps, rotation "); + sb.append(" fps, supportedRefreshRates "); + sb.append(Arrays.toString(supportedRefreshRates)); + sb.append(", rotation "); sb.append(rotation); sb.append(", density "); sb.append(logicalDensityDpi); diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 46b9e03..47ee52e 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1418,6 +1418,16 @@ public interface WindowManager extends ViewManager { public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; /** + * The preferred refresh rate for the window. + * + * This must be one of the supported refresh rates obtained for the display(s) the window + * is on. + * + * @see Display#getSupportedRefreshRates() + */ + public float preferredRefreshRate; + + /** * Control the visibility of the status bar. * * @see View#STATUS_BAR_VISIBLE @@ -1576,6 +1586,7 @@ public interface WindowManager extends ViewManager { out.writeString(packageName); TextUtils.writeToParcel(mTitle, out, parcelableFlags); out.writeInt(screenOrientation); + out.writeFloat(preferredRefreshRate); out.writeInt(systemUiVisibility); out.writeInt(subtreeSystemUiVisibility); out.writeInt(hasSystemUiListeners ? 1 : 0); @@ -1622,6 +1633,7 @@ public interface WindowManager extends ViewManager { packageName = in.readString(); mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); screenOrientation = in.readInt(); + preferredRefreshRate = in.readFloat(); systemUiVisibility = in.readInt(); subtreeSystemUiVisibility = in.readInt(); hasSystemUiListeners = in.readInt() != 0; @@ -1664,6 +1676,8 @@ public interface WindowManager extends ViewManager { /** {@hide} */ public static final int SURFACE_INSETS_CHANGED = 1<<20; /** {@hide} */ + public static final int PREFERRED_REFRESH_RATE_CHANGED = 1 << 21; + /** {@hide} */ public static final int EVERYTHING_CHANGED = 0xffffffff; // internal buffer to backup/restore parameters under compatibility mode. @@ -1776,6 +1790,11 @@ public interface WindowManager extends ViewManager { changes |= SCREEN_ORIENTATION_CHANGED; } + if (preferredRefreshRate != o.preferredRefreshRate) { + preferredRefreshRate = o.preferredRefreshRate; + changes |= PREFERRED_REFRESH_RATE_CHANGED; + } + if (systemUiVisibility != o.systemUiVisibility || subtreeSystemUiVisibility != o.subtreeSystemUiVisibility) { systemUiVisibility = o.systemUiVisibility; @@ -1884,6 +1903,10 @@ public interface WindowManager extends ViewManager { sb.append(" rotAnim="); sb.append(rotationAnimation); } + if (preferredRefreshRate != 0) { + sb.append(" preferredRefreshRate="); + sb.append(preferredRefreshRate); + } if (systemUiVisibility != 0) { sb.append(" sysui=0x"); sb.append(Integer.toHexString(systemUiVisibility)); diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index a5f9822..16554d3 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -113,6 +113,12 @@ abstract class DisplayDevice { } /** + * Sets the refresh rate, if supported. + */ + public void requestRefreshRateLocked(float refreshRate) { + } + + /** * Sets the display layer stack while in a transaction. */ public final void setLayerStackInTransactionLocked(int layerStack) { diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index c7f4f6a..f48428a 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -21,6 +21,9 @@ import android.util.DisplayMetrics; import android.view.Display; import android.view.Surface; +import java.util.Arrays; + +import libcore.util.EmptyArray; import libcore.util.Objects; /** @@ -124,6 +127,11 @@ final class DisplayDeviceInfo { public float refreshRate; /** + * The supported refresh rates of the display at the current resolution in frames per second. + */ + public float[] supportedRefreshRates = EmptyArray.FLOAT; + + /** * The nominal apparent density of the display in DPI used for layout calculations. * This density is sensitive to the viewing distance. A big TV and a tablet may have * the same apparent density even though the pixels on the TV are much bigger than @@ -230,6 +238,7 @@ final class DisplayDeviceInfo { && width == other.width && height == other.height && refreshRate == other.refreshRate + && Arrays.equals(supportedRefreshRates, other.supportedRefreshRates) && densityDpi == other.densityDpi && xDpi == other.xDpi && yDpi == other.yDpi @@ -255,6 +264,7 @@ final class DisplayDeviceInfo { width = other.width; height = other.height; refreshRate = other.refreshRate; + supportedRefreshRates = other.supportedRefreshRates; densityDpi = other.densityDpi; xDpi = other.xDpi; yDpi = other.yDpi; @@ -276,8 +286,9 @@ final class DisplayDeviceInfo { StringBuilder sb = new StringBuilder(); sb.append("DisplayDeviceInfo{\""); sb.append(name).append("\": ").append(width).append(" x ").append(height); - sb.append(", ").append(refreshRate).append(" fps, "); - sb.append("density ").append(densityDpi); + sb.append(", ").append(refreshRate).append(" fps"); + sb.append(", supportedRefreshRates ").append(Arrays.toString(supportedRefreshRates)); + sb.append(", density ").append(densityDpi); sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi"); sb.append(", appVsyncOff ").append(appVsyncOffsetNanos); sb.append(", presDeadline ").append(presentationDeadlineNanos); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 7d3738c..2dd150a 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -777,11 +777,14 @@ public final class DisplayManagerService extends SystemService { } } - private void setDisplayHasContentInternal(int displayId, boolean hasContent, - boolean inTraversal) { + private void setDisplayPropertiesInternal(int displayId, boolean hasContent, + float requestedRefreshRate, boolean inTraversal) { synchronized (mSyncRoot) { LogicalDisplay display = mLogicalDisplays.get(displayId); - if (display != null && display.hasContentLocked() != hasContent) { + if (display == null) { + return; + } + if (display.hasContentLocked() != hasContent) { if (DEBUG) { Slog.d(TAG, "Display " + displayId + " hasContent flag changed: " + "hasContent=" + hasContent + ", inTraversal=" + inTraversal); @@ -790,6 +793,14 @@ public final class DisplayManagerService extends SystemService { display.setHasContentLocked(hasContent); scheduleTraversalLocked(inTraversal); } + if (display.getRequestedRefreshRateLocked() != requestedRefreshRate) { + if (DEBUG) { + Slog.d(TAG, "Display " + displayId + " has requested a new refresh rate: " + + requestedRefreshRate + "fps"); + } + display.setRequestedRefreshRateLocked(requestedRefreshRate); + scheduleTraversalLocked(inTraversal); + } } } @@ -1482,8 +1493,9 @@ public final class DisplayManagerService extends SystemService { } @Override - public void setDisplayHasContent(int displayId, boolean hasContent, boolean inTraversal) { - setDisplayHasContentInternal(displayId, hasContent, inTraversal); + public void setDisplayProperties(int displayId, boolean hasContent, + float requestedRefreshRate, boolean inTraversal) { + setDisplayPropertiesInternal(displayId, hasContent, requestedRefreshRate, inTraversal); } } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 2bed143..4fd006d 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -21,6 +21,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.SystemProperties; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.view.Display; @@ -29,6 +30,7 @@ import android.view.Surface; import android.view.SurfaceControl; import java.io.PrintWriter; +import java.util.Arrays; /** * A display adapter for the local displays managed by Surface Flinger. @@ -88,10 +90,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { if (device == null) { // Display was added. device = new LocalDisplayDevice(displayToken, builtInDisplayId, - configs[activeConfig]); + configs, activeConfig); mDevices.put(builtInDisplayId, device); sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); - } else if (device.updatePhysicalDisplayInfoLocked(configs[activeConfig])) { + } else if (device.updatePhysicalDisplayInfoLocked(configs, activeConfig)) { // Display properties changed. sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); } @@ -127,21 +129,31 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final class LocalDisplayDevice extends DisplayDevice { private final int mBuiltInDisplayId; private final SurfaceControl.PhysicalDisplayInfo mPhys; + private final int mDefaultPhysicalDisplayInfo; private DisplayDeviceInfo mInfo; private boolean mHavePendingChanges; private int mState = Display.STATE_UNKNOWN; + private float[] mSupportedRefreshRates; + private int[] mRefreshRateConfigIndices; + private float mLastRequestedRefreshRate; public LocalDisplayDevice(IBinder displayToken, int builtInDisplayId, - SurfaceControl.PhysicalDisplayInfo phys) { + SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) { super(LocalDisplayAdapter.this, displayToken); mBuiltInDisplayId = builtInDisplayId; - mPhys = new SurfaceControl.PhysicalDisplayInfo(phys); + mPhys = new SurfaceControl.PhysicalDisplayInfo( + physicalDisplayInfos[activeDisplayInfo]); + mDefaultPhysicalDisplayInfo = activeDisplayInfo; + updateSupportedRefreshRatesLocked(physicalDisplayInfos, mPhys); } - public boolean updatePhysicalDisplayInfoLocked(SurfaceControl.PhysicalDisplayInfo phys) { - if (!mPhys.equals(phys)) { - mPhys.copyFrom(phys); + public boolean updatePhysicalDisplayInfoLocked( + SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, int activeDisplayInfo) { + SurfaceControl.PhysicalDisplayInfo newPhys = physicalDisplayInfos[activeDisplayInfo]; + if (!mPhys.equals(newPhys)) { + mPhys.copyFrom(newPhys); + updateSupportedRefreshRatesLocked(physicalDisplayInfos, mPhys); mHavePendingChanges = true; return true; } @@ -163,6 +175,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.width = mPhys.width; mInfo.height = mPhys.height; mInfo.refreshRate = mPhys.refreshRate; + mInfo.supportedRefreshRates = mSupportedRefreshRates; mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos; mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos; mInfo.state = mState; @@ -219,6 +232,26 @@ final class LocalDisplayAdapter extends DisplayAdapter { } @Override + public void requestRefreshRateLocked(float refreshRate) { + if (mLastRequestedRefreshRate == refreshRate) { + return; + } + mLastRequestedRefreshRate = refreshRate; + if (refreshRate != 0) { + final int N = mSupportedRefreshRates.length; + for (int i = 0; i < N; i++) { + if (refreshRate == mSupportedRefreshRates[i]) { + final int configIndex = mRefreshRateConfigIndices[i]; + SurfaceControl.setActiveConfig(getDisplayTokenLocked(), configIndex); + return; + } + } + Slog.w(TAG, "Requested refresh rate " + refreshRate + " is unsupported."); + } + SurfaceControl.setActiveConfig(getDisplayTokenLocked(), mDefaultPhysicalDisplayInfo); + } + + @Override public void dumpLocked(PrintWriter pw) { super.dumpLocked(pw); pw.println("mBuiltInDisplayId=" + mBuiltInDisplayId); @@ -230,6 +263,30 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo = null; sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); } + + private void updateSupportedRefreshRatesLocked( + SurfaceControl.PhysicalDisplayInfo[] physicalDisplayInfos, + SurfaceControl.PhysicalDisplayInfo activePhys) { + final int N = physicalDisplayInfos.length; + int idx = 0; + mSupportedRefreshRates = new float[N]; + mRefreshRateConfigIndices = new int[N]; + for (int i = 0; i < N; i++) { + final SurfaceControl.PhysicalDisplayInfo phys = physicalDisplayInfos[i]; + if (activePhys.width == phys.width + && activePhys.height == phys.height + && activePhys.density == phys.density + && activePhys.xDpi == phys.xDpi + && activePhys.yDpi == phys.yDpi) { + mSupportedRefreshRates[idx] = phys.refreshRate; + mRefreshRateConfigIndices[idx++] = i; + } + } + if (idx != N) { + mSupportedRefreshRates = Arrays.copyOfRange(mSupportedRefreshRates, 0, idx); + mRefreshRateConfigIndices = Arrays.copyOfRange(mRefreshRateConfigIndices, 0, idx); + } + } } private final class HotplugDisplayEventReceiver extends DisplayEventReceiver { diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 284780d..00ff1cf 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -22,6 +22,7 @@ import android.view.DisplayInfo; import android.view.Surface; import java.io.PrintWriter; +import java.util.Arrays; import java.util.List; import libcore.util.Objects; @@ -72,6 +73,9 @@ final class LogicalDisplay { // True if the logical display has unique content. private boolean mHasContent; + // The pending requested refresh rate. 0 if no request is pending. + private float mRequestedRefreshRate; + // Temporary rectangle used when needed. private final Rect mTempLayerStackRect = new Rect(); private final Rect mTempDisplayRect = new Rect(); @@ -210,6 +214,8 @@ final class LogicalDisplay { mBaseDisplayInfo.logicalHeight = deviceInfo.height; mBaseDisplayInfo.rotation = Surface.ROTATION_0; mBaseDisplayInfo.refreshRate = deviceInfo.refreshRate; + mBaseDisplayInfo.supportedRefreshRates = Arrays.copyOf( + deviceInfo.supportedRefreshRates, deviceInfo.supportedRefreshRates.length); mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi; mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi; mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi; @@ -253,6 +259,9 @@ final class LogicalDisplay { // Set the layer stack. device.setLayerStackInTransactionLocked(isBlanked ? BLANK_LAYER_STACK : mLayerStack); + // Set the refresh rate + device.requestRefreshRateLocked(mRequestedRefreshRate); + // Set the viewport. // This is the area of the logical display that we intend to show on the // display device. For now, it is always the full size of the logical display. @@ -328,6 +337,23 @@ final class LogicalDisplay { mHasContent = hasContent; } + /** + * Requests the given refresh rate. + * @param requestedRefreshRate The desired refresh rate. + */ + public void setRequestedRefreshRateLocked(float requestedRefreshRate) { + mRequestedRefreshRate = requestedRefreshRate; + } + + /** + * Gets the pending requested refresh rate. + * + * @return The pending refresh rate requested + */ + public float getRequestedRefreshRateLocked() { + return mRequestedRefreshRate; + } + public void dumpLocked(PrintWriter pw) { pw.println("mDisplayId=" + mDisplayId); pw.println("mLayerStack=" + mLayerStack); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index cdb5e58..304d2d4 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -619,6 +619,8 @@ public class WindowManagerService extends IWindowManager.Stub // Only set while traversing the default display based on its content. // Affects the behavior of mirroring on secondary displays. boolean mObscureApplicationContentOnSecondaryDisplays = false; + + float mPreferredRefreshRate = 0; } final LayoutFields mInnerFields = new LayoutFields(); @@ -9091,6 +9093,10 @@ public class WindowManagerService extends IWindowManager.Stub // Allow full screen keyguard presentation dialogs to be seen. mInnerFields.mDisplayHasContent = true; } + if (mInnerFields.mPreferredRefreshRate == 0 + && w.mAttrs.preferredRefreshRate != 0) { + mInnerFields.mPreferredRefreshRate = w.mAttrs.preferredRefreshRate; + } } } } @@ -9215,6 +9221,7 @@ public class WindowManagerService extends IWindowManager.Stub // Reset for each display. mInnerFields.mDisplayHasContent = false; + mInnerFields.mPreferredRefreshRate = 0; int repeats = 0; do { @@ -9434,8 +9441,8 @@ public class WindowManagerService extends IWindowManager.Stub updateResizingWindows(w); } - mDisplayManagerInternal.setDisplayHasContent(displayId, - mInnerFields.mDisplayHasContent, + mDisplayManagerInternal.setDisplayProperties(displayId, + mInnerFields.mDisplayHasContent, mInnerFields.mPreferredRefreshRate, true /* inTraversal, must call performTraversalInTrans... below */); getDisplayContentLocked(displayId).stopDimmingIfNeeded(); |
