Loading core/java/android/companion/virtual/computercontrol/IComputerControlSession.aidl +5 −9 Original line number Diff line number Diff line Loading @@ -16,12 +16,10 @@ package android.companion.virtual.computercontrol; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.IVirtualInputDevice; import android.companion.virtual.computercontrol.IInteractiveMirrorDisplay; import android.hardware.input.VirtualKeyEvent; import android.hardware.input.VirtualTouchEvent; import android.hardware.input.VirtualTouchscreenConfig; import android.view.Surface; /** * Interface for computer control session management. Loading @@ -39,11 +37,9 @@ interface IComputerControlSession { /** Injects a touch event into the trusted virtual display. */ void sendTouchEvent(in VirtualTouchEvent event); /** Creates a virtual display mirroring the trusted one and returns the display ID. */ int createMirrorDisplay(in VirtualDisplayConfig config, in IVirtualDisplayCallback callback); /** Creates a touchscreen associated with a mirror display. */ IVirtualInputDevice createMirrorDisplayTouchscreen(in VirtualTouchscreenConfig config); /** Creates an interactive virtual display, mirroring the trusted one. */ IInteractiveMirrorDisplay createInteractiveMirrorDisplay( int width, int height, in Surface surface); /** Closes this session. */ void close(); Loading core/java/android/companion/virtual/computercontrol/IInteractiveMirrorDisplay.aidl 0 → 100644 +36 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.companion.virtual.computercontrol; import android.hardware.input.VirtualTouchEvent; /** * A display, mirroring a computer control session display, and its associated touchscreen. * * @hide */ oneway interface IInteractiveMirrorDisplay { /** Resize the mirror display and updates the associated touchscreen. */ void resize(int width, int height); /** Injects a touch event into the mirror display. */ void sendTouchEvent(in VirtualTouchEvent event); /** Closes this mirror display and the associated touchscreen. */ void close(); } services/companion/java/com/android/server/companion/virtual/computercontrol/ComputerControlSessionImpl.java +32 −34 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.companion.virtual.IVirtualDeviceActivityListener; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.computercontrol.ComputerControlSessionParams; import android.companion.virtual.computercontrol.IComputerControlSession; import android.companion.virtual.computercontrol.IInteractiveMirrorDisplay; import android.content.AttributionSource; import android.content.ComponentName; import android.content.IntentSender; Loading @@ -43,8 +44,12 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; /** * A computer control session that encapsulates a {@link IVirtualDevice}. The device is created and Loading @@ -53,18 +58,21 @@ import java.util.Objects; final class ComputerControlSessionImpl extends IComputerControlSession.Stub { private final IBinder mAppToken; private final ComputerControlSessionParams mParams; private final IVirtualDevice mVirtualDevice; private final int mVirtualDisplayId; private final IVirtualInputDevice mVirtualTouchscreen; private final IVirtualInputDevice mVirtualDpad; private final IVirtualInputDevice mVirtualKeyboard; private final AtomicInteger mMirrorDisplayCounter = new AtomicInteger(0); ComputerControlSessionImpl(IBinder appToken, ComputerControlSessionParams params, AttributionSource attributionSource, PackageManager packageManager, ComputerControlSessionProcessor.VirtualDeviceFactory virtualDeviceFactory) { mAppToken = appToken; mParams = params; VirtualDeviceParams virtualDeviceParams = new VirtualDeviceParams.Builder() .setName(params.name) .setName(mParams.name) .setDevicePolicy(VirtualDeviceParams.POLICY_TYPE_RECENTS, VirtualDeviceParams.DEVICE_POLICY_CUSTOM) .build(); Loading @@ -76,7 +84,7 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { int displayFlags = DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED | DisplayManager.VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED; if (params.isDisplayAlwaysUnlocked) { if (mParams.isDisplayAlwaysUnlocked) { displayFlags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED; } Loading @@ -90,9 +98,9 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { new DisplayManagerGlobal.VirtualDisplayCallback(null, null); VirtualDisplayConfig virtualDisplayConfig = new VirtualDisplayConfig.Builder( params.name + "-display", params.displayWidthPx, params.displayHeightPx, params.displayDpi) .setSurface(params.displaySurface) mParams.name + "-display", mParams.displayWidthPx, mParams.displayHeightPx, mParams.displayDpi) .setSurface(mParams.displaySurface) .setFlags(displayFlags) .build(); Loading @@ -106,7 +114,7 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { mVirtualDevice.createVirtualDisplay( virtualDisplayConfig, virtualDisplayCallback)); String dpadName = params.name + "-dpad"; String dpadName = mParams.name + "-dpad"; VirtualDpadConfig virtualDpadConfig = new VirtualDpadConfig.Builder() .setAssociatedDisplayId(mVirtualDisplayId) Loading @@ -115,7 +123,7 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { mVirtualDpad = mVirtualDevice.createVirtualDpad( virtualDpadConfig, new Binder(dpadName)); String keyboardName = params.name + "-keyboard"; String keyboardName = mParams.name + "-keyboard"; VirtualKeyboardConfig virtualKeyboardConfig = new VirtualKeyboardConfig.Builder() .setAssociatedDisplayId(mVirtualDisplayId) Loading @@ -124,10 +132,10 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { mVirtualKeyboard = mVirtualDevice.createVirtualKeyboard( virtualKeyboardConfig, new Binder(keyboardName)); String touchscreenName = params.name + "-touchscreen"; String touchscreenName = mParams.name + "-touchscreen"; VirtualTouchscreenConfig virtualTouchscreenConfig = new VirtualTouchscreenConfig.Builder( params.displayWidthPx, params.displayHeightPx) mParams.displayWidthPx, mParams.displayHeightPx) .setAssociatedDisplayId(mVirtualDisplayId) .setInputDeviceName(touchscreenName) .build(); Loading Loading @@ -159,31 +167,21 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { } @Override public int createMirrorDisplay( @NonNull VirtualDisplayConfig config, @NonNull IVirtualDisplayCallback callback) throws RemoteException { Objects.requireNonNull(config); Objects.requireNonNull(callback); // The config in the app process is only used for the name and the surface, so we can // replace it here to ensure it only has the allowed properties and we don't leak // capabilities by creating the display with a clean identity (which we need for the mirror // display creation permission). VirtualDisplayConfig internalConfig = new VirtualDisplayConfig.Builder( config.getName(), config.getWidth(), config.getHeight(), config.getDensityDpi()) .setSurface(config.getSurface()) @NonNull public IInteractiveMirrorDisplay createInteractiveMirrorDisplay( int width, int height, @NonNull Surface surface) throws RemoteException { Objects.requireNonNull(surface); Display display = DisplayManagerGlobal.getInstance().getRealDisplay(mVirtualDisplayId); DisplayInfo displayInfo = new DisplayInfo(); display.getDisplayInfo(displayInfo); String name = mParams.name + "-display-mirror-" + mMirrorDisplayCounter.getAndIncrement(); VirtualDisplayConfig virtualDisplayConfig = new VirtualDisplayConfig.Builder(name, width, height, displayInfo.logicalDensityDpi) .setSurface(surface) .setDisplayIdToMirror(mVirtualDisplayId) .setFlags(DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) .build(); return Binder.withCleanCallingIdentity(() -> mVirtualDevice.createVirtualDisplay(internalConfig, callback)); } @Override public IVirtualInputDevice createMirrorDisplayTouchscreen( @NonNull VirtualTouchscreenConfig config) throws RemoteException { return mVirtualDevice.createVirtualTouchscreen( config, new Binder(config.getInputDeviceName())); return new InteractiveMirrorDisplayImpl(virtualDisplayConfig, mVirtualDevice); } @Override Loading services/companion/java/com/android/server/companion/virtual/computercontrol/InteractiveMirrorDisplayImpl.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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 com.android.server.companion.virtual.computercontrol; import android.annotation.NonNull; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.computercontrol.IInteractiveMirrorDisplay; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.IVirtualInputDevice; import android.hardware.input.VirtualTouchEvent; import android.hardware.input.VirtualTouchscreenConfig; import android.os.Binder; import android.os.RemoteException; import android.view.Display; import java.util.Objects; /** * A wrapper around a mirror virtual display and the associated touchscreen. */ final class InteractiveMirrorDisplayImpl extends IInteractiveMirrorDisplay.Stub { private final VirtualDisplayConfig mVirtualDisplayConfig; private final IVirtualDevice mVirtualDevice; private final VirtualDisplay mVirtualDisplay; private IVirtualInputDevice mVirtualTouchscreen; InteractiveMirrorDisplayImpl(VirtualDisplayConfig virtualDisplayConfig, IVirtualDevice virtualDevice) throws RemoteException { mVirtualDisplayConfig = virtualDisplayConfig; mVirtualDevice = virtualDevice; // This is used as a death detection token to release the display upon app death. We're in // the system process, so this won't happen, but this is OK because we already do death // detection in the virtual device based on the app token and closing it will also release // the display. // The same applies to the input devices. We can't reuse the app token there because it's // used as a map key for the virtual input devices. IVirtualDisplayCallback virtualDisplayCallback = new DisplayManagerGlobal.VirtualDisplayCallback(null, null); int displayId = Binder.withCleanCallingIdentity(() -> mVirtualDevice.createVirtualDisplay(virtualDisplayConfig, virtualDisplayCallback)); DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance(); mVirtualDisplay = displayManager.createVirtualDisplayWrapper( virtualDisplayConfig, virtualDisplayCallback, displayId); createTouchscreen(); } @Override public void resize(int width, int height) throws RemoteException { mVirtualDisplay.resize(width, height, mVirtualDisplayConfig.getDensityDpi()); // Since there is no way to resize a touchscreen, just recreate it. mVirtualTouchscreen.close(); createTouchscreen(); } @Override public void sendTouchEvent(@NonNull VirtualTouchEvent event) throws RemoteException { mVirtualTouchscreen.sendTouchEvent(Objects.requireNonNull(event)); } @Override public void close() throws RemoteException { mVirtualDisplay.release(); mVirtualTouchscreen.close(); } private void createTouchscreen() throws RemoteException { Display display = mVirtualDisplay.getDisplay(); String touchscreenName = display.getName() + "-touchscreen"; VirtualTouchscreenConfig virtualTouchscreenConfig = new VirtualTouchscreenConfig.Builder(display.getWidth(), display.getHeight()) .setAssociatedDisplayId(display.getDisplayId()) .setInputDeviceName(touchscreenName) .build(); mVirtualTouchscreen = mVirtualDevice.createVirtualTouchscreen( virtualTouchscreenConfig, new Binder(touchscreenName)); } } Loading
core/java/android/companion/virtual/computercontrol/IComputerControlSession.aidl +5 −9 Original line number Diff line number Diff line Loading @@ -16,12 +16,10 @@ package android.companion.virtual.computercontrol; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.IVirtualInputDevice; import android.companion.virtual.computercontrol.IInteractiveMirrorDisplay; import android.hardware.input.VirtualKeyEvent; import android.hardware.input.VirtualTouchEvent; import android.hardware.input.VirtualTouchscreenConfig; import android.view.Surface; /** * Interface for computer control session management. Loading @@ -39,11 +37,9 @@ interface IComputerControlSession { /** Injects a touch event into the trusted virtual display. */ void sendTouchEvent(in VirtualTouchEvent event); /** Creates a virtual display mirroring the trusted one and returns the display ID. */ int createMirrorDisplay(in VirtualDisplayConfig config, in IVirtualDisplayCallback callback); /** Creates a touchscreen associated with a mirror display. */ IVirtualInputDevice createMirrorDisplayTouchscreen(in VirtualTouchscreenConfig config); /** Creates an interactive virtual display, mirroring the trusted one. */ IInteractiveMirrorDisplay createInteractiveMirrorDisplay( int width, int height, in Surface surface); /** Closes this session. */ void close(); Loading
core/java/android/companion/virtual/computercontrol/IInteractiveMirrorDisplay.aidl 0 → 100644 +36 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.companion.virtual.computercontrol; import android.hardware.input.VirtualTouchEvent; /** * A display, mirroring a computer control session display, and its associated touchscreen. * * @hide */ oneway interface IInteractiveMirrorDisplay { /** Resize the mirror display and updates the associated touchscreen. */ void resize(int width, int height); /** Injects a touch event into the mirror display. */ void sendTouchEvent(in VirtualTouchEvent event); /** Closes this mirror display and the associated touchscreen. */ void close(); }
services/companion/java/com/android/server/companion/virtual/computercontrol/ComputerControlSessionImpl.java +32 −34 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.companion.virtual.IVirtualDeviceActivityListener; import android.companion.virtual.VirtualDeviceParams; import android.companion.virtual.computercontrol.ComputerControlSessionParams; import android.companion.virtual.computercontrol.IComputerControlSession; import android.companion.virtual.computercontrol.IInteractiveMirrorDisplay; import android.content.AttributionSource; import android.content.ComponentName; import android.content.IntentSender; Loading @@ -43,8 +44,12 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; /** * A computer control session that encapsulates a {@link IVirtualDevice}. The device is created and Loading @@ -53,18 +58,21 @@ import java.util.Objects; final class ComputerControlSessionImpl extends IComputerControlSession.Stub { private final IBinder mAppToken; private final ComputerControlSessionParams mParams; private final IVirtualDevice mVirtualDevice; private final int mVirtualDisplayId; private final IVirtualInputDevice mVirtualTouchscreen; private final IVirtualInputDevice mVirtualDpad; private final IVirtualInputDevice mVirtualKeyboard; private final AtomicInteger mMirrorDisplayCounter = new AtomicInteger(0); ComputerControlSessionImpl(IBinder appToken, ComputerControlSessionParams params, AttributionSource attributionSource, PackageManager packageManager, ComputerControlSessionProcessor.VirtualDeviceFactory virtualDeviceFactory) { mAppToken = appToken; mParams = params; VirtualDeviceParams virtualDeviceParams = new VirtualDeviceParams.Builder() .setName(params.name) .setName(mParams.name) .setDevicePolicy(VirtualDeviceParams.POLICY_TYPE_RECENTS, VirtualDeviceParams.DEVICE_POLICY_CUSTOM) .build(); Loading @@ -76,7 +84,7 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { int displayFlags = DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED | DisplayManager.VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED; if (params.isDisplayAlwaysUnlocked) { if (mParams.isDisplayAlwaysUnlocked) { displayFlags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED; } Loading @@ -90,9 +98,9 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { new DisplayManagerGlobal.VirtualDisplayCallback(null, null); VirtualDisplayConfig virtualDisplayConfig = new VirtualDisplayConfig.Builder( params.name + "-display", params.displayWidthPx, params.displayHeightPx, params.displayDpi) .setSurface(params.displaySurface) mParams.name + "-display", mParams.displayWidthPx, mParams.displayHeightPx, mParams.displayDpi) .setSurface(mParams.displaySurface) .setFlags(displayFlags) .build(); Loading @@ -106,7 +114,7 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { mVirtualDevice.createVirtualDisplay( virtualDisplayConfig, virtualDisplayCallback)); String dpadName = params.name + "-dpad"; String dpadName = mParams.name + "-dpad"; VirtualDpadConfig virtualDpadConfig = new VirtualDpadConfig.Builder() .setAssociatedDisplayId(mVirtualDisplayId) Loading @@ -115,7 +123,7 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { mVirtualDpad = mVirtualDevice.createVirtualDpad( virtualDpadConfig, new Binder(dpadName)); String keyboardName = params.name + "-keyboard"; String keyboardName = mParams.name + "-keyboard"; VirtualKeyboardConfig virtualKeyboardConfig = new VirtualKeyboardConfig.Builder() .setAssociatedDisplayId(mVirtualDisplayId) Loading @@ -124,10 +132,10 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { mVirtualKeyboard = mVirtualDevice.createVirtualKeyboard( virtualKeyboardConfig, new Binder(keyboardName)); String touchscreenName = params.name + "-touchscreen"; String touchscreenName = mParams.name + "-touchscreen"; VirtualTouchscreenConfig virtualTouchscreenConfig = new VirtualTouchscreenConfig.Builder( params.displayWidthPx, params.displayHeightPx) mParams.displayWidthPx, mParams.displayHeightPx) .setAssociatedDisplayId(mVirtualDisplayId) .setInputDeviceName(touchscreenName) .build(); Loading Loading @@ -159,31 +167,21 @@ final class ComputerControlSessionImpl extends IComputerControlSession.Stub { } @Override public int createMirrorDisplay( @NonNull VirtualDisplayConfig config, @NonNull IVirtualDisplayCallback callback) throws RemoteException { Objects.requireNonNull(config); Objects.requireNonNull(callback); // The config in the app process is only used for the name and the surface, so we can // replace it here to ensure it only has the allowed properties and we don't leak // capabilities by creating the display with a clean identity (which we need for the mirror // display creation permission). VirtualDisplayConfig internalConfig = new VirtualDisplayConfig.Builder( config.getName(), config.getWidth(), config.getHeight(), config.getDensityDpi()) .setSurface(config.getSurface()) @NonNull public IInteractiveMirrorDisplay createInteractiveMirrorDisplay( int width, int height, @NonNull Surface surface) throws RemoteException { Objects.requireNonNull(surface); Display display = DisplayManagerGlobal.getInstance().getRealDisplay(mVirtualDisplayId); DisplayInfo displayInfo = new DisplayInfo(); display.getDisplayInfo(displayInfo); String name = mParams.name + "-display-mirror-" + mMirrorDisplayCounter.getAndIncrement(); VirtualDisplayConfig virtualDisplayConfig = new VirtualDisplayConfig.Builder(name, width, height, displayInfo.logicalDensityDpi) .setSurface(surface) .setDisplayIdToMirror(mVirtualDisplayId) .setFlags(DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) .build(); return Binder.withCleanCallingIdentity(() -> mVirtualDevice.createVirtualDisplay(internalConfig, callback)); } @Override public IVirtualInputDevice createMirrorDisplayTouchscreen( @NonNull VirtualTouchscreenConfig config) throws RemoteException { return mVirtualDevice.createVirtualTouchscreen( config, new Binder(config.getInputDeviceName())); return new InteractiveMirrorDisplayImpl(virtualDisplayConfig, mVirtualDevice); } @Override Loading
services/companion/java/com/android/server/companion/virtual/computercontrol/InteractiveMirrorDisplayImpl.java 0 → 100644 +98 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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 com.android.server.companion.virtual.computercontrol; import android.annotation.NonNull; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.computercontrol.IInteractiveMirrorDisplay; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.IVirtualInputDevice; import android.hardware.input.VirtualTouchEvent; import android.hardware.input.VirtualTouchscreenConfig; import android.os.Binder; import android.os.RemoteException; import android.view.Display; import java.util.Objects; /** * A wrapper around a mirror virtual display and the associated touchscreen. */ final class InteractiveMirrorDisplayImpl extends IInteractiveMirrorDisplay.Stub { private final VirtualDisplayConfig mVirtualDisplayConfig; private final IVirtualDevice mVirtualDevice; private final VirtualDisplay mVirtualDisplay; private IVirtualInputDevice mVirtualTouchscreen; InteractiveMirrorDisplayImpl(VirtualDisplayConfig virtualDisplayConfig, IVirtualDevice virtualDevice) throws RemoteException { mVirtualDisplayConfig = virtualDisplayConfig; mVirtualDevice = virtualDevice; // This is used as a death detection token to release the display upon app death. We're in // the system process, so this won't happen, but this is OK because we already do death // detection in the virtual device based on the app token and closing it will also release // the display. // The same applies to the input devices. We can't reuse the app token there because it's // used as a map key for the virtual input devices. IVirtualDisplayCallback virtualDisplayCallback = new DisplayManagerGlobal.VirtualDisplayCallback(null, null); int displayId = Binder.withCleanCallingIdentity(() -> mVirtualDevice.createVirtualDisplay(virtualDisplayConfig, virtualDisplayCallback)); DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance(); mVirtualDisplay = displayManager.createVirtualDisplayWrapper( virtualDisplayConfig, virtualDisplayCallback, displayId); createTouchscreen(); } @Override public void resize(int width, int height) throws RemoteException { mVirtualDisplay.resize(width, height, mVirtualDisplayConfig.getDensityDpi()); // Since there is no way to resize a touchscreen, just recreate it. mVirtualTouchscreen.close(); createTouchscreen(); } @Override public void sendTouchEvent(@NonNull VirtualTouchEvent event) throws RemoteException { mVirtualTouchscreen.sendTouchEvent(Objects.requireNonNull(event)); } @Override public void close() throws RemoteException { mVirtualDisplay.release(); mVirtualTouchscreen.close(); } private void createTouchscreen() throws RemoteException { Display display = mVirtualDisplay.getDisplay(); String touchscreenName = display.getName() + "-touchscreen"; VirtualTouchscreenConfig virtualTouchscreenConfig = new VirtualTouchscreenConfig.Builder(display.getWidth(), display.getHeight()) .setAssociatedDisplayId(display.getDisplayId()) .setInputDeviceName(touchscreenName) .build(); mVirtualTouchscreen = mVirtualDevice.createVirtualTouchscreen( virtualTouchscreenConfig, new Binder(touchscreenName)); } }