diff options
11 files changed, 777 insertions, 72 deletions
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index 65f45d895027..ea5cc7f2e8bc 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -634,17 +634,39 @@ public final class DisplayManager { public VirtualDisplay createVirtualDisplay(@NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface, int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { - return createVirtualDisplay(null /* projection */, name, width, height, densityDpi, surface, - flags, callback, handler, null /* uniqueId */); + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width, + height, densityDpi); + builder.setFlags(flags); + if (surface != null) { + builder.setSurface(surface); + } + return createVirtualDisplay(null /* projection */, builder.build(), callback, handler); } + // TODO : Remove this hidden API after remove all callers. (Refer to MultiDisplayService) /** @hide */ public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection, @NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface, int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler, @Nullable String uniqueId) { - return mGlobal.createVirtualDisplay(mContext, projection, - name, width, height, densityDpi, surface, flags, callback, handler, uniqueId); + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width, + height, densityDpi); + builder.setFlags(flags); + if (uniqueId != null) { + builder.setUniqueId(uniqueId); + } + if (surface != null) { + builder.setSurface(surface); + } + return createVirtualDisplay(projection, builder.build(), callback, handler); + } + + /** @hide */ + public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection, + @NonNull VirtualDisplayConfig virtualDisplayConfig, + @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { + return mGlobal.createVirtualDisplay(mContext, projection, virtualDisplayConfig, callback, + handler); } /** diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 526db85b47d4..4d645e6052a7 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -451,35 +451,26 @@ public final class DisplayManagerGlobal { } } - public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection, - String name, int width, int height, int densityDpi, Surface surface, int flags, - VirtualDisplay.Callback callback, Handler handler, String uniqueId) { - if (TextUtils.isEmpty(name)) { - throw new IllegalArgumentException("name must be non-null and non-empty"); - } - if (width <= 0 || height <= 0 || densityDpi <= 0) { - throw new IllegalArgumentException("width, height, and densityDpi must be " - + "greater than 0"); - } - + public VirtualDisplay createVirtualDisplay(@NonNull Context context, MediaProjection projection, + @NonNull VirtualDisplayConfig virtualDisplayConfig, VirtualDisplay.Callback callback, + Handler handler) { VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler); IMediaProjection projectionToken = projection != null ? projection.getProjection() : null; int displayId; try { - displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken, - context.getPackageName(), name, width, height, densityDpi, surface, flags, - uniqueId); + displayId = mDm.createVirtualDisplay(virtualDisplayConfig, callbackWrapper, + projectionToken, context.getPackageName()); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } if (displayId < 0) { - Log.e(TAG, "Could not create virtual display: " + name); + Log.e(TAG, "Could not create virtual display: " + virtualDisplayConfig.getName()); return null; } Display display = getRealDisplay(displayId); if (display == null) { Log.wtf(TAG, "Could not obtain display info for newly created " - + "virtual display: " + name); + + "virtual display: " + virtualDisplayConfig.getName()); try { mDm.releaseVirtualDisplay(callbackWrapper); } catch (RemoteException ex) { @@ -487,7 +478,8 @@ public final class DisplayManagerGlobal { } return null; } - return new VirtualDisplay(this, display, callbackWrapper, surface); + return new VirtualDisplay(this, display, callbackWrapper, + virtualDisplayConfig.getSurface()); } public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index d22188ec5d7f..c697106d0c17 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -22,6 +22,7 @@ import android.hardware.display.BrightnessConfiguration; import android.hardware.display.Curve; import android.hardware.display.IDisplayManagerCallback; import android.hardware.display.IVirtualDisplayCallback; +import android.hardware.display.VirtualDisplayConfig; import android.hardware.display.WifiDisplay; import android.hardware.display.WifiDisplayStatus; import android.media.projection.IMediaProjection; @@ -71,9 +72,9 @@ interface IDisplayManager { // Requires CAPTURE_VIDEO_OUTPUT, CAPTURE_SECURE_VIDEO_OUTPUT, or an appropriate // MediaProjection token for certain combinations of flags. - int createVirtualDisplay(in IVirtualDisplayCallback callback, - in IMediaProjection projectionToken, String packageName, String name, - int width, int height, int densityDpi, in Surface surface, int flags, String uniqueId); + int createVirtualDisplay(in VirtualDisplayConfig virtualDisplayConfig, + in IVirtualDisplayCallback callback, in IMediaProjection projectionToken, + String packageName); // No permissions required, but must be same Uid as the creator. void resizeVirtualDisplay(in IVirtualDisplayCallback token, diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.aidl b/core/java/android/hardware/display/VirtualDisplayConfig.aidl new file mode 100644 index 000000000000..c28f1dfb9806 --- /dev/null +++ b/core/java/android/hardware/display/VirtualDisplayConfig.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display; + +parcelable VirtualDisplayConfig; diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java new file mode 100644 index 000000000000..10e1c7c2e0df --- /dev/null +++ b/core/java/android/hardware/display/VirtualDisplayConfig.java @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.display; + +import static android.view.Display.DEFAULT_DISPLAY; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.media.projection.MediaProjection; +import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.Surface; + +import com.android.internal.util.DataClass; + +/** + * Holds configuration used to create {@link VirtualDisplay} instances. See + * {@link MediaProjection#createVirtualDisplay(VirtualDisplayConfig, VirtualDisplay.Callback, Handler)}. + * + * @hide + */ +@DataClass(genParcelable = true, genAidl = true, genBuilder = true) +public final class VirtualDisplayConfig implements Parcelable { + /** + * The name of the virtual display, must be non-empty. + */ + @NonNull + private String mName; + + /** + * The width of the virtual display in pixels. Must be greater than 0. + */ + @IntRange(from = 1) + private int mWidth; + + /** + * The height of the virtual display in pixels. Must be greater than 0. + */ + @IntRange(from = 1) + private int mHeight; + + /** + * The density of the virtual display in dpi. Must be greater than 0. + */ + @IntRange(from = 1) + private int mDensityDpi; + + /** + * A combination of virtual display flags. + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}, + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION}, + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE}, + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, + * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. + */ + private int mFlags = 0; + + /** + * The surface to which the content of the virtual display should be rendered, or null if + * there is none initially. + */ + @Nullable + private Surface mSurface = null; + + /** + * The unique identifier for the display. Shouldn't be displayed to the user. + * @hide + */ + @Nullable + private String mUniqueId = null; + + /** + * The id of the display that the virtual display should mirror, or + * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially. + */ + private int mDisplayIdToMirror = DEFAULT_DISPLAY; + + + + // Code below generated by codegen v1.0.15. + // + // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code + // + // To regenerate run: + // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off + + + @DataClass.Generated.Member + /* package-private */ VirtualDisplayConfig( + @NonNull String name, + @IntRange(from = 1) int width, + @IntRange(from = 1) int height, + @IntRange(from = 1) int densityDpi, + int flags, + @Nullable Surface surface, + @Nullable String uniqueId, + int displayIdToMirror) { + this.mName = name; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mName); + this.mWidth = width; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mWidth, + "from", 1); + this.mHeight = height; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mHeight, + "from", 1); + this.mDensityDpi = densityDpi; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mDensityDpi, + "from", 1); + this.mFlags = flags; + this.mSurface = surface; + this.mUniqueId = uniqueId; + this.mDisplayIdToMirror = displayIdToMirror; + + // onConstructed(); // You can define this method to get a callback + } + + /** + * The name of the virtual display, must be non-empty. + */ + @DataClass.Generated.Member + public @NonNull String getName() { + return mName; + } + + /** + * The width of the virtual display in pixels. Must be greater than 0. + */ + @DataClass.Generated.Member + public @IntRange(from = 1) int getWidth() { + return mWidth; + } + + /** + * The height of the virtual display in pixels. Must be greater than 0. + */ + @DataClass.Generated.Member + public @IntRange(from = 1) int getHeight() { + return mHeight; + } + + /** + * The density of the virtual display in dpi. Must be greater than 0. + */ + @DataClass.Generated.Member + public @IntRange(from = 1) int getDensityDpi() { + return mDensityDpi; + } + + /** + * A combination of virtual display flags. + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}, + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION}, + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE}, + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, + * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. + */ + @DataClass.Generated.Member + public int getFlags() { + return mFlags; + } + + /** + * The surface to which the content of the virtual display should be rendered, or null if + * there is none initially. + */ + @DataClass.Generated.Member + public @Nullable Surface getSurface() { + return mSurface; + } + + /** + * The unique identifier for the display. Shouldn't be displayed to the user. + * + * @hide + */ + @DataClass.Generated.Member + public @Nullable String getUniqueId() { + return mUniqueId; + } + + /** + * The id of the display that the virtual display should mirror, or + * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially. + */ + @DataClass.Generated.Member + public int getDisplayIdToMirror() { + return mDisplayIdToMirror; + } + + @Override + @DataClass.Generated.Member + public void writeToParcel(@NonNull Parcel dest, int flags) { + // You can override field parcelling by defining methods like: + // void parcelFieldName(Parcel dest, int flags) { ... } + + int flg = 0; + if (mSurface != null) flg |= 0x20; + if (mUniqueId != null) flg |= 0x40; + dest.writeInt(flg); + dest.writeString(mName); + dest.writeInt(mWidth); + dest.writeInt(mHeight); + dest.writeInt(mDensityDpi); + dest.writeInt(mFlags); + if (mSurface != null) dest.writeTypedObject(mSurface, flags); + if (mUniqueId != null) dest.writeString(mUniqueId); + dest.writeInt(mDisplayIdToMirror); + } + + @Override + @DataClass.Generated.Member + public int describeContents() { return 0; } + + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ VirtualDisplayConfig(@NonNull Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + int flg = in.readInt(); + String name = in.readString(); + int width = in.readInt(); + int height = in.readInt(); + int densityDpi = in.readInt(); + int flags = in.readInt(); + Surface surface = (flg & 0x20) == 0 ? null : (Surface) in.readTypedObject(Surface.CREATOR); + String uniqueId = (flg & 0x40) == 0 ? null : in.readString(); + int displayIdToMirror = in.readInt(); + + this.mName = name; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mName); + this.mWidth = width; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mWidth, + "from", 1); + this.mHeight = height; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mHeight, + "from", 1); + this.mDensityDpi = densityDpi; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mDensityDpi, + "from", 1); + this.mFlags = flags; + this.mSurface = surface; + this.mUniqueId = uniqueId; + this.mDisplayIdToMirror = displayIdToMirror; + + // onConstructed(); // You can define this method to get a callback + } + + @DataClass.Generated.Member + public static final @NonNull Parcelable.Creator<VirtualDisplayConfig> CREATOR + = new Parcelable.Creator<VirtualDisplayConfig>() { + @Override + public VirtualDisplayConfig[] newArray(int size) { + return new VirtualDisplayConfig[size]; + } + + @Override + public VirtualDisplayConfig createFromParcel(@NonNull Parcel in) { + return new VirtualDisplayConfig(in); + } + }; + + /** + * A builder for {@link VirtualDisplayConfig} + */ + @SuppressWarnings("WeakerAccess") + @DataClass.Generated.Member + public static final class Builder { + + private @NonNull String mName; + private @IntRange(from = 1) int mWidth; + private @IntRange(from = 1) int mHeight; + private @IntRange(from = 1) int mDensityDpi; + private int mFlags; + private @Nullable Surface mSurface; + private @Nullable String mUniqueId; + private int mDisplayIdToMirror; + + private long mBuilderFieldsSet = 0L; + + /** + * Creates a new Builder. + * + * @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. + */ + public Builder( + @NonNull String name, + @IntRange(from = 1) int width, + @IntRange(from = 1) int height, + @IntRange(from = 1) int densityDpi) { + mName = name; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mName); + mWidth = width; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mWidth, + "from", 1); + mHeight = height; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mHeight, + "from", 1); + mDensityDpi = densityDpi; + com.android.internal.util.AnnotationValidations.validate( + IntRange.class, null, mDensityDpi, + "from", 1); + } + + /** + * The name of the virtual display, must be non-empty. + */ + @DataClass.Generated.Member + public @NonNull Builder setName(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x1; + mName = value; + return this; + } + + /** + * The width of the virtual display in pixels. Must be greater than 0. + */ + @DataClass.Generated.Member + public @NonNull Builder setWidth(@IntRange(from = 1) int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x2; + mWidth = value; + return this; + } + + /** + * The height of the virtual display in pixels. Must be greater than 0. + */ + @DataClass.Generated.Member + public @NonNull Builder setHeight(@IntRange(from = 1) int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x4; + mHeight = value; + return this; + } + + /** + * The density of the virtual display in dpi. Must be greater than 0. + */ + @DataClass.Generated.Member + public @NonNull Builder setDensityDpi(@IntRange(from = 1) int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x8; + mDensityDpi = value; + return this; + } + + /** + * A combination of virtual display flags. + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}, + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION}, + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE}, + * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, + * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. + */ + @DataClass.Generated.Member + public @NonNull Builder setFlags(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x10; + mFlags = value; + return this; + } + + /** + * The surface to which the content of the virtual display should be rendered, or null if + * there is none initially. + */ + @DataClass.Generated.Member + public @NonNull Builder setSurface(@NonNull Surface value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x20; + mSurface = value; + return this; + } + + /** + * The unique identifier for the display. Shouldn't be displayed to the user. + * + * @hide + */ + @DataClass.Generated.Member + public @NonNull Builder setUniqueId(@NonNull String value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x40; + mUniqueId = value; + return this; + } + + /** + * The id of the display that the virtual display should mirror, or + * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially. + */ + @DataClass.Generated.Member + public @NonNull Builder setDisplayIdToMirror(int value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x80; + mDisplayIdToMirror = value; + return this; + } + + /** Builds the instance. This builder should not be touched after calling this! */ + public @NonNull VirtualDisplayConfig build() { + checkNotUsed(); + mBuilderFieldsSet |= 0x100; // Mark builder used + + if ((mBuilderFieldsSet & 0x10) == 0) { + mFlags = 0; + } + if ((mBuilderFieldsSet & 0x20) == 0) { + mSurface = null; + } + if ((mBuilderFieldsSet & 0x40) == 0) { + mUniqueId = null; + } + if ((mBuilderFieldsSet & 0x80) == 0) { + mDisplayIdToMirror = DEFAULT_DISPLAY; + } + VirtualDisplayConfig o = new VirtualDisplayConfig( + mName, + mWidth, + mHeight, + mDensityDpi, + mFlags, + mSurface, + mUniqueId, + mDisplayIdToMirror); + return o; + } + + private void checkNotUsed() { + if ((mBuilderFieldsSet & 0x100) != 0) { + throw new IllegalStateException( + "This Builder should not be reused. Use a new Builder instance instead"); + } + } + } + + @DataClass.Generated( + time = 1585179350902L, + codegenVersion = "1.0.15", + sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java", + inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange(from=1L) int mWidth\nprivate @android.annotation.IntRange(from=1L) int mHeight\nprivate @android.annotation.IntRange(from=1L) int mDensityDpi\nprivate int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate int mDisplayIdToMirror\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() {} + + + //@formatter:on + // End of generated code + +} diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java index 632cfb0f1e30..37e141537c79 100644 --- a/media/java/android/media/projection/MediaProjection.java +++ b/media/java/android/media/projection/MediaProjection.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; +import android.hardware.display.VirtualDisplayConfig; import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionCallback; import android.os.Handler; @@ -100,11 +101,18 @@ public final class MediaProjection { int width, int height, int dpi, boolean isSecure, @Nullable Surface surface, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); - int flags = isSecure ? DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE : 0; - return dm.createVirtualDisplay(this, name, width, height, dpi, surface, - flags | DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR | - DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION, callback, handler, - null /* uniqueId */); + int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR + | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; + if (isSecure) { + flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; + } + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width, + height, dpi); + builder.setFlags(flags); + if (surface != null) { + builder.setSurface(surface); + } + return dm.createVirtualDisplay(this, builder.build(), callback, handler); } /** @@ -133,9 +141,35 @@ public final class MediaProjection { public VirtualDisplay createVirtualDisplay(@NonNull String name, int width, int height, int dpi, int flags, @Nullable Surface surface, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { - DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); - return dm.createVirtualDisplay(this, name, width, height, dpi, surface, flags, callback, - handler, null /* uniqueId */); + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width, + height, dpi); + builder.setFlags(flags); + if (surface != null) { + builder.setSurface(surface); + } + return createVirtualDisplay(builder.build(), callback, handler); + } + + /** + * Creates a {@link android.hardware.display.VirtualDisplay} to capture the + * contents of the screen. + * + * @param virtualDisplayConfig The arguments for the virtual display configuration. See + * {@link VirtualDisplayConfig} for using it. + * @param callback Callback to call when the virtual display's state + * changes, or null if none. + * @param handler The {@link android.os.Handler} on which the callback should be + * invoked, or null if the callback should be invoked on the calling + * thread's main {@link android.os.Looper}. + * + * @see android.hardware.display.VirtualDisplay + * @hide + */ + @Nullable + public VirtualDisplay createVirtualDisplay(@NonNull VirtualDisplayConfig virtualDisplayConfig, + @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { + DisplayManager dm = mContext.getSystemService(DisplayManager.class); + return dm.createVirtualDisplay(this, virtualDisplayConfig, callback, handler); } /** diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 63a8d7c92441..696daca79092 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -19,6 +19,7 @@ package com.android.server.display; import android.graphics.Rect; import android.hardware.display.DisplayViewport; import android.os.IBinder; +import android.view.Display; import android.view.DisplayAddress; import android.view.Surface; import android.view.SurfaceControl; @@ -78,6 +79,13 @@ abstract class DisplayDevice { } /** + * Gets the id of the display to mirror. + */ + public int getDisplayIdToMirrorLocked() { + return Display.DEFAULT_DISPLAY; + } + + /** * Gets the name of the display device. * * @return The display device name. diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index a23205124f74..3afbf661f97e 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -57,6 +57,7 @@ import android.hardware.display.DisplayedContentSamplingAttributes; import android.hardware.display.IDisplayManager; import android.hardware.display.IDisplayManagerCallback; import android.hardware.display.IVirtualDisplayCallback; +import android.hardware.display.VirtualDisplayConfig; import android.hardware.display.WifiDisplayStatus; import android.hardware.input.InputManagerInternal; import android.media.projection.IMediaProjection; @@ -794,8 +795,8 @@ public final class DisplayManagerService extends SystemService { } private int createVirtualDisplayInternal(IVirtualDisplayCallback callback, - IMediaProjection projection, int callingUid, String packageName, String name, int width, - int height, int densityDpi, Surface surface, int flags, String uniqueId) { + IMediaProjection projection, int callingUid, String packageName, Surface surface, + int flags, VirtualDisplayConfig virtualDisplayConfig) { synchronized (mSyncRoot) { if (mVirtualDisplayAdapter == null) { Slog.w(TAG, "Rejecting request to create private virtual display " @@ -804,8 +805,8 @@ public final class DisplayManagerService extends SystemService { } DisplayDevice device = mVirtualDisplayAdapter.createVirtualDisplayLocked( - callback, projection, callingUid, packageName, name, width, height, densityDpi, - surface, flags, uniqueId); + callback, projection, callingUid, packageName, surface, flags, + virtualDisplayConfig); if (device == null) { return -1; } @@ -1480,8 +1481,8 @@ public final class DisplayManagerService extends SystemService { if (!ownContent) { if (display != null && !display.hasContentLocked()) { // If the display does not have any content of its own, then - // automatically mirror the default logical display contents. - display = null; + // automatically mirror the requested logical display contents if possible. + display = mLogicalDisplays.get(device.getDisplayIdToMirrorLocked()); } if (display == null) { display = mLogicalDisplays.get(Display.DEFAULT_DISPLAY); @@ -1729,6 +1730,28 @@ public final class DisplayManagerService extends SystemService { } } + @VisibleForTesting + int getDisplayIdToMirrorInternal(int displayId) { + synchronized (mSyncRoot) { + LogicalDisplay display = mLogicalDisplays.get(displayId); + if (display != null) { + DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked(); + return displayDevice.getDisplayIdToMirrorLocked(); + } + return Display.INVALID_DISPLAY; + } + } + + @VisibleForTesting + Surface getVirtualDisplaySurfaceInternal(IBinder appToken) { + synchronized (mSyncRoot) { + if (mVirtualDisplayAdapter == null) { + return null; + } + return mVirtualDisplayAdapter.getVirtualDisplaySurfaceLocked(appToken); + } + } + private final class DisplayManagerHandler extends Handler { public DisplayManagerHandler(Looper looper) { super(looper, null, true /*async*/); @@ -2050,10 +2073,8 @@ public final class DisplayManagerService extends SystemService { } @Override // Binder call - public int createVirtualDisplay(IVirtualDisplayCallback callback, - IMediaProjection projection, String packageName, String name, - int width, int height, int densityDpi, Surface surface, int flags, - String uniqueId) { + public int createVirtualDisplay(VirtualDisplayConfig virtualDisplayConfig, + IVirtualDisplayCallback callback, IMediaProjection projection, String packageName) { final int callingUid = Binder.getCallingUid(); if (!validatePackageName(callingUid, packageName)) { throw new SecurityException("packageName must match the calling uid"); @@ -2061,13 +2082,12 @@ public final class DisplayManagerService extends SystemService { if (callback == null) { throw new IllegalArgumentException("appToken must not be null"); } - if (TextUtils.isEmpty(name)) { - throw new IllegalArgumentException("name must be non-null and non-empty"); - } - if (width <= 0 || height <= 0 || densityDpi <= 0) { - throw new IllegalArgumentException("width, height, and densityDpi must be " - + "greater than 0"); + if (virtualDisplayConfig == null) { + throw new IllegalArgumentException("virtualDisplayConfig must not be null"); } + final Surface surface = virtualDisplayConfig.getSurface(); + int flags = virtualDisplayConfig.getFlags(); + if (surface != null && surface.isSingleBuffered()) { throw new IllegalArgumentException("Surface can't be single-buffered"); } @@ -2128,7 +2148,7 @@ public final class DisplayManagerService extends SystemService { final long token = Binder.clearCallingIdentity(); try { return createVirtualDisplayInternal(callback, projection, callingUid, packageName, - name, width, height, densityDpi, surface, flags, uniqueId); + surface, flags, virtualDisplayConfig); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index f4f2eadfaa8e..ccd88483593a 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -28,6 +28,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPO import android.content.Context; import android.hardware.display.IVirtualDisplayCallback; +import android.hardware.display.VirtualDisplayConfig; import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionCallback; import android.os.Handler; @@ -84,22 +85,24 @@ public class VirtualDisplayAdapter extends DisplayAdapter { } public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback, - IMediaProjection projection, int ownerUid, String ownerPackageName, String name, - int width, int height, int densityDpi, Surface surface, int flags, String uniqueId) { + IMediaProjection projection, int ownerUid, String ownerPackageName, Surface surface, + int flags, VirtualDisplayConfig virtualDisplayConfig) { + String name = virtualDisplayConfig.getName(); boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0; IBinder appToken = callback.asBinder(); IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure); final String baseUniqueId = UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ","; final int uniqueIndex = getNextUniqueIndex(baseUniqueId); + String uniqueId = virtualDisplayConfig.getUniqueId(); if (uniqueId == null) { uniqueId = baseUniqueId + uniqueIndex; } else { uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId; } VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken, - ownerUid, ownerPackageName, name, width, height, densityDpi, surface, flags, - new Callback(callback, mHandler), uniqueId, uniqueIndex); + ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler), + uniqueId, uniqueIndex, virtualDisplayConfig); mVirtualDisplayDevices.put(appToken, device); @@ -127,6 +130,14 @@ public class VirtualDisplayAdapter extends DisplayAdapter { } } + @VisibleForTesting + Surface getVirtualDisplaySurfaceLocked(IBinder appToken) { + VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); + if (device != null) { + return device.getSurfaceLocked(); + } + return null; + } public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) { VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); @@ -214,20 +225,21 @@ public class VirtualDisplayAdapter extends DisplayAdapter { private int mUniqueIndex; private Display.Mode mMode; private boolean mIsDisplayOn; + private int mDisplayIdToMirror; public VirtualDisplayDevice(IBinder displayToken, IBinder appToken, - int ownerUid, String ownerPackageName, - String name, int width, int height, int densityDpi, Surface surface, int flags, - Callback callback, String uniqueId, int uniqueIndex) { + int ownerUid, String ownerPackageName, Surface surface, int flags, + Callback callback, String uniqueId, int uniqueIndex, + VirtualDisplayConfig virtualDisplayConfig) { super(VirtualDisplayAdapter.this, displayToken, uniqueId); mAppToken = appToken; mOwnerUid = ownerUid; mOwnerPackageName = ownerPackageName; - mName = name; - mWidth = width; - mHeight = height; - mMode = createMode(width, height, REFRESH_RATE); - mDensityDpi = densityDpi; + mName = virtualDisplayConfig.getName(); + mWidth = virtualDisplayConfig.getWidth(); + mHeight = virtualDisplayConfig.getHeight(); + mMode = createMode(mWidth, mHeight, REFRESH_RATE); + mDensityDpi = virtualDisplayConfig.getDensityDpi(); mSurface = surface; mFlags = flags; mCallback = callback; @@ -235,6 +247,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { mPendingChanges |= PENDING_SURFACE_CHANGE; mUniqueIndex = uniqueIndex; mIsDisplayOn = surface != null; + mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror(); } @Override @@ -260,6 +273,16 @@ public class VirtualDisplayAdapter extends DisplayAdapter { } @Override + public int getDisplayIdToMirrorLocked() { + return mDisplayIdToMirror; + } + + @VisibleForTesting + Surface getSurfaceLocked() { + return mSurface; + } + + @Override public boolean hasStableUniqueId() { return false; } @@ -332,6 +355,7 @@ public class VirtualDisplayAdapter extends DisplayAdapter { pw.println("mFlags=" + mFlags); pw.println("mDisplayState=" + Display.stateToString(mDisplayState)); pw.println("mStopped=" + mStopped); + pw.println("mDisplayIdToMirror=" + mDisplayIdToMirror); } diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java index a16dbb726613..3f2b5c231dca 100644 --- a/services/core/java/com/android/server/vr/Vr2dDisplay.java +++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java @@ -11,6 +11,7 @@ import android.content.IntentFilter; import android.graphics.PixelFormat; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; +import android.hardware.display.VirtualDisplayConfig; import android.media.ImageReader; import android.os.Handler; import android.os.RemoteException; @@ -295,10 +296,12 @@ class Vr2dDisplay { flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( + DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi); + builder.setUniqueId(UNIQUE_DISPLAY_ID); + builder.setFlags(flags); mVirtualDisplay = mDisplayManager.createVirtualDisplay(null /* projection */, - DISPLAY_NAME, mVirtualDisplayWidth, mVirtualDisplayHeight, mVirtualDisplayDpi, - null /* surface */, flags, null /* callback */, null /* handler */, - UNIQUE_DISPLAY_ID); + builder.build(), null /* callback */, null /* handler */); if (mVirtualDisplay != null) { updateDisplayId(mVirtualDisplay.getDisplay().getDisplayId()); 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 702718569871..7ad39b440e14 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -34,14 +34,17 @@ import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; import android.hardware.display.IVirtualDisplayCallback; +import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.InputManagerInternal; import android.os.Handler; import android.os.IBinder; import android.view.Display; import android.view.DisplayInfo; +import android.view.Surface; import android.view.SurfaceControl; import androidx.test.InstrumentationRegistry; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -67,6 +70,8 @@ import java.util.stream.LongStream; public class DisplayManagerServiceTest { private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1; private static final long SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS = 10; + private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display"; + private static final String PACKAGE_NAME = "com.android.frameworks.servicestests"; private Context mContext; @@ -97,6 +102,7 @@ public class DisplayManagerServiceTest { @Mock InputManagerInternal mMockInputManagerInternal; @Mock IVirtualDisplayCallback.Stub mMockAppToken; + @Mock IVirtualDisplayCallback.Stub mMockAppToken2; @Mock WindowManagerInternal mMockWindowManagerInternal; @Mock LightsManager mMockLightsManager; @Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter; @@ -135,10 +141,12 @@ public class DisplayManagerServiceTest { int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH; when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); - int displayId = bs.createVirtualDisplay(mMockAppToken /* callback */, - null /* projection */, "com.android.frameworks.servicestests", - "Test Virtual Display", width, height, dpi, null /* surface */, flags /* flags */, - uniqueId); + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( + VIRTUAL_DISPLAY_NAME, width, height, dpi); + builder.setUniqueId(uniqueId); + builder.setFlags(flags); + int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, + null /* projection */, PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -241,10 +249,12 @@ public class DisplayManagerServiceTest { int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT; when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); - int displayId = bs.createVirtualDisplay(mMockAppToken /* callback */, - null /* projection */, "com.android.frameworks.servicestests", - "Test Virtual Display", width, height, dpi, null /* surface */, flags /* flags */, - uniqueId); + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( + VIRTUAL_DISPLAY_NAME, width, height, dpi); + builder.setFlags(flags); + builder.setUniqueId(uniqueId); + int displayId = bs.createVirtualDisplay(builder.build(), mMockAppToken /* callback */, + null /* projection */, PACKAGE_NAME); displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); @@ -409,6 +419,87 @@ public class DisplayManagerServiceTest { assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels); } + /** + * Tests that the virtual display is created with + * {@link VirtualDisplayConfig.Builder#setDisplayIdToMirror(int)} + */ + @Test + @FlakyTest(bugId = 127687569) + public void testCreateVirtualDisplay_displayIdToMirror() throws Exception { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + registerDefaultDisplays(displayManager); + + // This is effectively the DisplayManager service published to ServiceManager. + DisplayManagerService.BinderService binderService = displayManager.new BinderService(); + + final String uniqueId = "uniqueId --- displayIdToMirrorTest"; + final int width = 600; + final int height = 800; + final int dpi = 320; + + when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( + VIRTUAL_DISPLAY_NAME, width, height, dpi); + builder.setUniqueId(uniqueId); + final int firstDisplayId = binderService.createVirtualDisplay(builder.build(), + mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + + // The second virtual display requests to mirror the first virtual display. + final String uniqueId2 = "uniqueId --- displayIdToMirrorTest #2"; + when(mMockAppToken2.asBinder()).thenReturn(mMockAppToken2); + final VirtualDisplayConfig.Builder builder2 = new VirtualDisplayConfig.Builder( + VIRTUAL_DISPLAY_NAME, width, height, dpi).setUniqueId(uniqueId2); + builder2.setUniqueId(uniqueId2); + builder2.setDisplayIdToMirror(firstDisplayId); + final int secondDisplayId = binderService.createVirtualDisplay(builder2.build(), + mMockAppToken2 /* callback */, null /* projection */, PACKAGE_NAME); + displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); + + // flush the handler + displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); + + // The displayId to mirror should be a default display if there is none initially. + assertEquals(displayManager.getDisplayIdToMirrorInternal(firstDisplayId), + Display.DEFAULT_DISPLAY); + assertEquals(displayManager.getDisplayIdToMirrorInternal(secondDisplayId), + firstDisplayId); + } + + /** + * Tests that the virtual display is created with + * {@link VirtualDisplayConfig.Builder#setSurface(Surface)} + */ + @Test + @FlakyTest(bugId = 127687569) + public void testCreateVirtualDisplay_setSurface() throws Exception { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + registerDefaultDisplays(displayManager); + + // This is effectively the DisplayManager service published to ServiceManager. + DisplayManagerService.BinderService binderService = displayManager.new BinderService(); + + final String uniqueId = "uniqueId --- setSurface"; + final int width = 600; + final int height = 800; + final int dpi = 320; + final Surface surface = new Surface(); + + when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); + final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( + VIRTUAL_DISPLAY_NAME, width, height, dpi); + builder.setSurface(surface); + builder.setUniqueId(uniqueId); + final int displayId = binderService.createVirtualDisplay(builder.build(), + mMockAppToken /* callback */, null /* projection */, PACKAGE_NAME); + + displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); + + // flush the handler + displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); + + assertEquals(displayManager.getVirtualDisplaySurfaceInternal(mMockAppToken), surface); + } + private void registerDefaultDisplays(DisplayManagerService displayManager) { Handler handler = displayManager.getDisplayHandler(); // Would prefer to call displayManager.onStart() directly here but it performs binderService |