Merge "Variable refresh rate for virtual display"

This commit is contained in:
Huihong Luo
2023-01-31 21:12:41 +00:00
committed by Android (Google) Code Review
7 changed files with 190 additions and 18 deletions

View File

@@ -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);

View File

@@ -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 */);
}

View File

@@ -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() {}

View File

@@ -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);
}
/**

View File

@@ -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);
}
}

View File

@@ -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 },

View File

@@ -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