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