diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 0d024b1d32008..bace45bccbf44 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -16,21 +16,32 @@ package android.companion.virtual; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.companion.AssociationInfo; import android.content.Context; import android.graphics.Point; +import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; +import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.VirtualKeyboard; import android.hardware.input.VirtualMouse; import android.hardware.input.VirtualTouchscreen; import android.os.Binder; +import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.view.Surface; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; /** * System level service for managing virtual devices. @@ -44,6 +55,31 @@ public final class VirtualDeviceManager { private static final boolean DEBUG = false; private static final String LOG_TAG = "VirtualDeviceManager"; + /** @hide */ + @IntDef(prefix = "DISPLAY_FLAG_", + flag = true, + value = {DISPLAY_FLAG_TRUSTED}) + @Retention(RetentionPolicy.SOURCE) + @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) + public @interface DisplayFlags {} + + /** + * Indicates that the display is trusted to show system decorations and receive inputs without + * users' touch. + * + * @see DisplayManager#VIRTUAL_DISPLAY_FLAG_TRUSTED + * @hide // TODO(b/194949534): Unhide this API + */ + public static final int DISPLAY_FLAG_TRUSTED = 1; + + private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS = + DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC + | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT + | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY + | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL + | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH + | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP; + private final IVirtualDeviceManager mService; private final Context mContext; @@ -92,6 +128,56 @@ public final class VirtualDeviceManager { mVirtualDevice = virtualDevice; } + /** + * Creates a virtual display for this virtual device. All displays created on the same + * device belongs to the same display group. + * + * @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 surface The surface to which the content of the virtual display should + * be rendered, or null if there is none initially. The surface can also be set later using + * {@link VirtualDisplay#setSurface(Surface)}. + * @param flags Either 0, or {@link #DISPLAY_FLAG_TRUSTED}. + * @param callback Callback to call when the state of the {@link VirtualDisplay} changes + * @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. + * @return The newly created virtual display, or {@code null} if the application could + * not create the virtual display. + * + * @see DisplayManager#createVirtualDisplay + * @hide + */ + // TODO(b/194949534): Unhide this API + // Suppress "ExecutorRegistration" because DisplayManager.createVirtualDisplay takes a + // handler + @SuppressLint("ExecutorRegistration") + @Nullable + public VirtualDisplay createVirtualDisplay( + int width, + int height, + int densityDpi, + @Nullable Surface surface, + @DisplayFlags int flags, + @Nullable Handler handler, + @Nullable VirtualDisplay.Callback callback) { + // TODO(b/205343547): Handle display groups properly instead of creating a new display + // group for every new virtual display created using this API. + // belongs to the same display group. + DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); + // DisplayManager will call into VirtualDeviceManagerInternal to register the + // created displays. + return displayManager.createVirtualDisplay( + mVirtualDevice, + new VirtualDisplayConfig.Builder( + getVirtualDisplayName(), width, height, densityDpi) + .setSurface(surface) + .setFlags(getVirtualDisplayFlags(flags)) + .build(), + callback, + handler); + } + /** * Closes the virtual device, stopping and tearing down any virtual displays, * audio policies, and event injection that's currently in progress. @@ -186,5 +272,25 @@ public final class VirtualDeviceManager { throw e.rethrowFromSystemServer(); } } + + private int getVirtualDisplayFlags(@DisplayFlags int flags) { + int virtualDisplayFlags = DEFAULT_VIRTUAL_DISPLAY_FLAGS; + if ((flags & DISPLAY_FLAG_TRUSTED) != 0) { + virtualDisplayFlags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; + } + return virtualDisplayFlags; + } + + private String getVirtualDisplayName() { + try { + // Currently this just use the association ID, which means all of the virtual + // displays created using the same virtual device will have the same name. The name + // should only be used for informational purposes, and not for identifying the + // display in code. + return "VirtualDevice_" + mVirtualDevice.getAssociationId(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } } diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 022da4361be41..64a45a5f58617 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -55,6 +55,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @VisibleForTesting final List mVirtualDisplayIds = new ArrayList<>(); private final OnDeviceCloseListener mListener; + private final IBinder mAppToken; VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token, int ownerUid, OnDeviceCloseListener listener) { @@ -69,6 +70,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mGenericWindowPolicyController = new GenericWindowPolicyController(FLAG_SECURE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); mOwnerUid = ownerUid; + mAppToken = token; if (inputController == null) { mInputController = new InputController(mVirtualDeviceLock); } else { @@ -90,6 +92,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub @Override // Binder call public void close() { mListener.onClose(mAssociationInfo.getId()); + mAppToken.unlinkToDeath(this, 0); mInputController.close(); }