Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6c57176e authored by Huihong Luo's avatar Huihong Luo
Browse files

Variable refresh rate for virtual display

A new method is added for creating a virtual display with a requested
refresh rate.

Bug: 241286579
Test: atest android.display.cts.VirtualDisplayTest
Change-Id: I617a2f87f73697ecc6d7803cabb3b5bb5068ef40
parent 5c85be46
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -19231,7 +19231,9 @@ package android.hardware.display {
  public final class DisplayManager {
    method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int);
    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, float, @Nullable android.view.Surface, int);
    method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable android.hardware.display.VirtualDisplay.Callback, @Nullable android.os.Handler);
    method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, float, @Nullable android.view.Surface, int, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback);
    method public android.view.Display getDisplay(int);
    method public android.view.Display[] getDisplays();
    method public android.view.Display[] getDisplays(String);
+89 −0
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ import java.util.concurrent.Executor;
public final class DisplayManager {
    private static final String TAG = "DisplayManager";
    private static final boolean DEBUG = false;
    private static final boolean ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE = false;

    private final Context mContext;
    private final DisplayManagerGlobal mGlobal;
@@ -936,6 +937,24 @@ public final class DisplayManager {
        return createVirtualDisplay(name, width, height, densityDpi, surface, flags, null, null);
    }

    /**
     * Creates a virtual display.
     *
     * @see #createVirtualDisplay(String, int, int, int, float, Surface, int,
     * Handler, VirtualDisplay.Callback)
     */
    @Nullable
    public VirtualDisplay createVirtualDisplay(@NonNull String name,
            @IntRange(from = 1) int width,
            @IntRange(from = 1) int height,
            @IntRange(from = 1) int densityDpi,
            float requestedRefreshRate,
            @Nullable Surface surface,
            @VirtualDisplayFlag int flags) {
        return createVirtualDisplay(name, width, height, densityDpi, requestedRefreshRate,
                surface, flags, null, null);
    }

    /**
     * Creates a virtual display.
     * <p>
@@ -987,12 +1006,82 @@ public final class DisplayManager {
            @VirtualDisplayFlag int flags,
            @Nullable VirtualDisplay.Callback callback,
            @Nullable Handler handler) {
        return createVirtualDisplay(name, width, height, densityDpi, 0.0f, surface,
                flags, handler, callback);
    }

    /**
     * Creates a virtual display.
     * <p>
     * The content of a virtual display is rendered to a {@link Surface} provided
     * by the application.
     * </p><p>
     * The virtual display should be {@link VirtualDisplay#release released}
     * when no longer needed.  Because a virtual display renders to a surface
     * provided by the application, it will be released automatically when the
     * process terminates and all remaining windows on it will be forcibly removed.
     * </p><p>
     * The behavior of the virtual display depends on the flags that are provided
     * to this method.  By default, virtual displays are created to be private,
     * non-presentation and unsecure.  Permissions may be required to use certain flags.
     * </p><p>
     * As of {@link android.os.Build.VERSION_CODES#KITKAT_WATCH}, the surface may
     * be attached or detached dynamically using {@link VirtualDisplay#setSurface}.
     * Previously, the surface had to be non-null when {@link #createVirtualDisplay}
     * was called and could not be changed for the lifetime of the display.
     * </p><p>
     * Detaching the surface that backs a virtual display has a similar effect to
     * turning off the screen.
     * </p>
     *
     * @param name The name of the virtual display, must be non-empty.
     * @param width The width of the virtual display in pixels, must be greater than 0.
     * @param height The height of the virtual display in pixels, must be greater than 0.
     * @param densityDpi The density of the virtual display in dpi, must be greater than 0.
     * @param requestedRefreshRate The requested refresh rate in frames per second.
     * For best results, specify a divisor of the physical refresh rate, e.g., 30 or 60 on
     * 120hz display. If an arbitrary refresh rate is specified, the rate will be rounded
     * up or down to a divisor of the physical display. If 0 is specified, the virtual
     * display is refreshed at the physical display refresh rate.
     * @param surface The surface to which the content of the virtual display should
     * be rendered, or null if there is none initially.
     * @param flags A combination of virtual display flags:
     * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION},
     * {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY},
     * or {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}.
     * @param handler The handler on which the listener should be invoked, or null
     * if the listener should be invoked on the calling thread's looper.
     * @param callback Callback to call when the state of the {@link VirtualDisplay} changes
     * @return The newly created virtual display, or null if the application could
     * not create the virtual display.
     *
     * @throws SecurityException if the caller does not have permission to create
     * a virtual display with the specified flags.
     */
    @Nullable
    public VirtualDisplay createVirtualDisplay(@NonNull String name,
            @IntRange(from = 1) int width,
            @IntRange(from = 1) int height,
            @IntRange(from = 1) int densityDpi,
            float requestedRefreshRate,
            @Nullable Surface surface,
            @VirtualDisplayFlag int flags,
            @Nullable Handler handler,
            @Nullable VirtualDisplay.Callback callback) {
        if (!ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE && requestedRefreshRate != 0.0f) {
            Slog.e(TAG, "Please turn on ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE to use the new api");
            return null;
        }

        final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
                height, densityDpi);
        builder.setFlags(flags);
        if (surface != null) {
            builder.setSurface(surface);
        }
        if (requestedRefreshRate != 0.0f) {
            builder.setRequestedRefreshRate(requestedRefreshRate);
        }
        return createVirtualDisplay(null /* projection */, builder.build(), callback, handler,
                null /* windowContext */);
    }
+46 −6
Original line number Diff line number Diff line
@@ -109,6 +109,13 @@ public final class VirtualDisplayConfig implements Parcelable {
    @DataClass.PluralOf("displayCategory")
    @NonNull private List<String> mDisplayCategories = new ArrayList<>();

    /**
     * The refresh rate of a virtual display in frames per second.
     * If this value is non-zero, this is the requested refresh rate to set.
     * If this value is zero, the system chooses a default refresh rate.
     */
    private float mRequestedRefreshRate = 0.0f;



    // Code below generated by codegen v1.0.23.
@@ -135,7 +142,8 @@ public final class VirtualDisplayConfig implements Parcelable {
            @Nullable String uniqueId,
            int displayIdToMirror,
            boolean windowManagerMirroring,
            @NonNull List<String> displayCategories) {
            @NonNull List<String> displayCategories,
            float requestedRefreshRate) {
        this.mName = name;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mName);
@@ -161,6 +169,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        this.mDisplayCategories = displayCategories;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mDisplayCategories);
        this.mRequestedRefreshRate = requestedRefreshRate;

        // onConstructed(); // You can define this method to get a callback
    }
@@ -256,6 +265,16 @@ public final class VirtualDisplayConfig implements Parcelable {
        return mDisplayCategories;
    }

    /**
     * The refresh rate of a virtual display in frames per second.
     * If this value is none zero, this is the requested refresh rate to set.
     * If this value is zero, the system chooses a default refresh rate.
     */
    @DataClass.Generated.Member
    public float getRequestedRefreshRate() {
        return mRequestedRefreshRate;
    }

    @Override
    @DataClass.Generated.Member
    public void writeToParcel(@NonNull Parcel dest, int flags) {
@@ -276,6 +295,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        if (mUniqueId != null) dest.writeString(mUniqueId);
        dest.writeInt(mDisplayIdToMirror);
        dest.writeStringList(mDisplayCategories);
        dest.writeFloat(mRequestedRefreshRate);
    }

    @Override
@@ -301,6 +321,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        int displayIdToMirror = in.readInt();
        List<String> displayCategories = new ArrayList<>();
        in.readStringList(displayCategories);
        float requestedRefreshRate = in.readFloat();

        this.mName = name;
        com.android.internal.util.AnnotationValidations.validate(
@@ -327,6 +348,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        this.mDisplayCategories = displayCategories;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mDisplayCategories);
        this.mRequestedRefreshRate = requestedRefreshRate;

        // onConstructed(); // You can define this method to get a callback
    }
@@ -362,6 +384,7 @@ public final class VirtualDisplayConfig implements Parcelable {
        private int mDisplayIdToMirror;
        private boolean mWindowManagerMirroring;
        private @NonNull List<String> mDisplayCategories;
        private float mRequestedRefreshRate;

        private long mBuilderFieldsSet = 0L;

@@ -528,10 +551,23 @@ public final class VirtualDisplayConfig implements Parcelable {
            return this;
        }

        /**
         * The refresh rate of a virtual display in frames per second.
         * If this value is none zero, this is the requested refresh rate to set.
         * If this value is zero, the system chooses a default refresh rate.
         */
        @DataClass.Generated.Member
        public @NonNull Builder setRequestedRefreshRate(float value) {
            checkNotUsed();
            mBuilderFieldsSet |= 0x400;
            mRequestedRefreshRate = value;
            return this;
        }

        /** Builds the instance. This builder should not be touched after calling this! */
        public @NonNull VirtualDisplayConfig build() {
            checkNotUsed();
            mBuilderFieldsSet |= 0x400; // Mark builder used
            mBuilderFieldsSet |= 0x800; // Mark builder used

            if ((mBuilderFieldsSet & 0x10) == 0) {
                mFlags = 0;
@@ -551,6 +587,9 @@ public final class VirtualDisplayConfig implements Parcelable {
            if ((mBuilderFieldsSet & 0x200) == 0) {
                mDisplayCategories = new ArrayList<>();
            }
            if ((mBuilderFieldsSet & 0x400) == 0) {
                mRequestedRefreshRate = 0.0f;
            }
            VirtualDisplayConfig o = new VirtualDisplayConfig(
                    mName,
                    mWidth,
@@ -561,12 +600,13 @@ public final class VirtualDisplayConfig implements Parcelable {
                    mUniqueId,
                    mDisplayIdToMirror,
                    mWindowManagerMirroring,
                    mDisplayCategories);
                    mDisplayCategories,
                    mRequestedRefreshRate);
            return o;
        }

        private void checkNotUsed() {
            if ((mBuilderFieldsSet & 0x400) != 0) {
            if ((mBuilderFieldsSet & 0x800) != 0) {
                throw new IllegalStateException(
                        "This Builder should not be reused. Use a new Builder instance instead");
            }
@@ -574,10 +614,10 @@ public final class VirtualDisplayConfig implements Parcelable {
    }

    @DataClass.Generated(
            time = 1668534501320L,
            time = 1671047069703L,
            codegenVersion = "1.0.23",
            sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java",
            inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate @android.hardware.display.DisplayManager.VirtualDisplayFlag int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate  int mDisplayIdToMirror\nprivate  boolean mWindowManagerMirroring\nprivate @com.android.internal.util.DataClass.PluralOf(\"displayCategory\") @android.annotation.NonNull java.util.List<java.lang.String> mDisplayCategories\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
            inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate @android.hardware.display.DisplayManager.VirtualDisplayFlag int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate  int mDisplayIdToMirror\nprivate  boolean mWindowManagerMirroring\nprivate @com.android.internal.util.DataClass.PluralOf(\"displayCategory\") @android.annotation.NonNull java.util.List<java.lang.String> mDisplayCategories\nprivate  float mRequestedRefreshRate\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)")
    @Deprecated
    private void __metadata() {}

+21 −2
Original line number Diff line number Diff line
@@ -28,7 +28,8 @@ import java.util.Objects;
 * Calls into SurfaceFlinger for Display creation and deletion.
 */
public class DisplayControl {
    private static native IBinder nativeCreateDisplay(String name, boolean secure);
    private static native IBinder nativeCreateDisplay(String name, boolean secure,
            float requestedRefreshRate);
    private static native void nativeDestroyDisplay(IBinder displayToken);
    private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
    private static native long[] nativeGetPhysicalDisplayIds();
@@ -46,7 +47,25 @@ public class DisplayControl {
     */
    public static IBinder createDisplay(String name, boolean secure) {
        Objects.requireNonNull(name, "name must not be null");
        return nativeCreateDisplay(name, secure);
        return nativeCreateDisplay(name, secure, 0.0f);
    }

    /**
     * Create a display in SurfaceFlinger.
     *
     * @param name The name of the display
     * @param secure Whether this display is secure.
     * @param requestedRefreshRate The requested refresh rate in frames per second.
     * For best results, specify a divisor of the physical refresh rate, e.g., 30 or 60 on
     * 120hz display. If an arbitrary refresh rate is specified, the rate will be rounded
     * up or down to a divisor of the physical display. If 0 is specified, the virtual
     * display is refreshed at the physical display refresh rate.
     * @return The token reference for the display in SurfaceFlinger.
     */
    public static IBinder createDisplay(String name, boolean secure,
            float requestedRefreshRate) {
        Objects.requireNonNull(name, "name must not be null");
        return nativeCreateDisplay(name, secure, requestedRefreshRate);
    }

    /**
+26 −6
Original line number Diff line number Diff line
@@ -106,7 +106,8 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
        String name = virtualDisplayConfig.getName();
        boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0;
        IBinder appToken = callback.asBinder();
        IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure);
        IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure,
                virtualDisplayConfig.getRequestedRefreshRate());
        final String baseUniqueId =
                UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ",";
        final int uniqueIndex = getNextUniqueIndex(baseUniqueId);
@@ -247,6 +248,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
        private int mWidth;
        private int mHeight;
        private int mDensityDpi;
        private float mRequestedRefreshRate;
        private Surface mSurface;
        private DisplayDeviceInfo mInfo;
        private int mDisplayState;
@@ -270,8 +272,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
            mName = virtualDisplayConfig.getName();
            mWidth = virtualDisplayConfig.getWidth();
            mHeight = virtualDisplayConfig.getHeight();
            mMode = createMode(mWidth, mHeight, REFRESH_RATE);
            mDensityDpi = virtualDisplayConfig.getDensityDpi();
            mRequestedRefreshRate = virtualDisplayConfig.getRequestedRefreshRate();
            mMode = createMode(mWidth, mHeight, getRefreshRate());
            mSurface = surface;
            mFlags = flags;
            mCallback = callback;
@@ -410,7 +413,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
                sendTraversalRequestLocked();
                mWidth = width;
                mHeight = height;
                mMode = createMode(width, height, REFRESH_RATE);
                mMode = createMode(width, height, getRefreshRate());
                mDensityDpi = densityDpi;
                mInfo = null;
                mPendingChanges |= PENDING_RESIZE;
@@ -438,9 +441,9 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
            pw.println("mStopped=" + mStopped);
            pw.println("mDisplayIdToMirror=" + mDisplayIdToMirror);
            pw.println("mWindowManagerMirroring=" + mIsWindowManagerMirroring);
            pw.println("mRequestedRefreshRate=" + mRequestedRefreshRate);
        }


        @Override
        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
            if (mInfo == null) {
@@ -456,7 +459,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
                mInfo.densityDpi = mDensityDpi;
                mInfo.xDpi = mDensityDpi;
                mInfo.yDpi = mDensityDpi;
                mInfo.presentationDeadlineNanos = 1000000000L / (int) REFRESH_RATE; // 1 frame
                mInfo.presentationDeadlineNanos = 1000000000L / (int) getRefreshRate(); // 1 frame
                mInfo.flags = 0;
                if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
                    mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
@@ -554,6 +557,10 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
            }
            return mInfo;
        }

        private float getRefreshRate() {
            return (mRequestedRefreshRate != 0.0f) ? mRequestedRefreshRate : REFRESH_RATE;
        }
    }

    private static class Callback extends Handler {
@@ -632,6 +639,19 @@ public class VirtualDisplayAdapter extends DisplayAdapter {

    @VisibleForTesting
    public interface SurfaceControlDisplayFactory {
        public IBinder createDisplay(String name, boolean secure);
        /**
         * Create a virtual display in SurfaceFlinger.
         *
         * @param name The name of the display
         * @param secure Whether this display is secure.
         * @param requestedRefreshRate
         *     The refresh rate, frames per second, to request on the virtual display.
         *     It should be a divisor of refresh rate of the leader physical display
         *     that drives VSYNC, e.g. 30/60fps on 120fps display. If an arbitrary refresh
         *     rate is specified, SurfaceFlinger rounds up or down to match a divisor of
         *     the refresh rate of the leader physical display.
         * @return The token reference for the display in SurfaceFlinger.
         */
        IBinder createDisplay(String name, boolean secure, float requestedRefreshRate);
    }
}
Loading