diff options
7 files changed, 190 insertions, 18 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index d9c3529d1c7b..53512f250d34 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -19321,7 +19321,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); diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 08238ca4365e..d13a97dc6881 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -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; @@ -938,6 +939,24 @@ public final class DisplayManager { /** * 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> * The content of a virtual display is rendered to a {@link Surface} provided * by the application. @@ -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 */); } diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java index abd647fe2e5d..f6a2e33c7164 100644 --- a/core/java/android/hardware/display/VirtualDisplayConfig.java +++ b/core/java/android/hardware/display/VirtualDisplayConfig.java @@ -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() {} diff --git a/services/core/java/com/android/server/display/DisplayControl.java b/services/core/java/com/android/server/display/DisplayControl.java index fa9a100afd04..f229d0f71d35 100644 --- a/services/core/java/com/android/server/display/DisplayControl.java +++ b/services/core/java/com/android/server/display/DisplayControl.java @@ -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); } /** diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index ddeaa1b8e125..364d53ba3c10 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -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); } } diff --git a/services/core/jni/com_android_server_display_DisplayControl.cpp b/services/core/jni/com_android_server_display_DisplayControl.cpp index ec423241918a..db7fced0822a 100644 --- a/services/core/jni/com_android_server_display_DisplayControl.cpp +++ b/services/core/jni/com_android_server_display_DisplayControl.cpp @@ -23,9 +23,11 @@ namespace android { -static jobject nativeCreateDisplay(JNIEnv* env, jclass clazz, jstring nameObj, jboolean secure) { +static jobject nativeCreateDisplay(JNIEnv* env, jclass clazz, jstring nameObj, jboolean secure, + jfloat requestedRefreshRate) { ScopedUtfChars name(env, nameObj); - sp<IBinder> token(SurfaceComposerClient::createDisplay(String8(name.c_str()), bool(secure))); + sp<IBinder> token(SurfaceComposerClient::createDisplay(String8(name.c_str()), bool(secure), + requestedRefreshRate)); return javaObjectForIBinder(env, token); } @@ -134,7 +136,7 @@ static jobject nativeGetPhysicalDisplayToken(JNIEnv* env, jclass clazz, jlong ph static const JNINativeMethod sDisplayMethods[] = { // clang-format off - {"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;", + {"nativeCreateDisplay", "(Ljava/lang/String;ZF)Landroid/os/IBinder;", (void*)nativeCreateDisplay }, {"nativeDestroyDisplay", "(Landroid/os/IBinder;)V", (void*)nativeDestroyDisplay }, diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 2d252cbbbd9c..9a43762278c5 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -157,7 +157,7 @@ public class DisplayManagerServiceTest { VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context, Handler handler, DisplayAdapter.Listener displayAdapterListener) { return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener, - (String name, boolean secure) -> mMockDisplayToken); + (String name, boolean secure, float refreshRate) -> mMockDisplayToken); } @Override |