summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt2
-rw-r--r--core/java/android/hardware/display/DisplayManager.java89
-rw-r--r--core/java/android/hardware/display/VirtualDisplayConfig.java52
-rw-r--r--services/core/java/com/android/server/display/DisplayControl.java23
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java32
-rw-r--r--services/core/jni/com_android_server_display_DisplayControl.cpp8
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java2
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