Loading core/java/android/hardware/input/IVirtualInputDevice.aidl +6 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,12 @@ interface IVirtualInputDevice { */ int getInputDeviceId(); /** * Returns the ID of the display that this virtual input device is associated with, or * {@code INVALID_DISPLAY} if not associated with any display. */ int getAssociatedDisplayId(); /** * Injects a virtual dpad key event. */ Loading services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +21 −89 Original line number Diff line number Diff line Loading @@ -195,7 +195,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final int mDeviceId; @Nullable private final String mPersistentDeviceId; private final InputController mInputController; private final VirtualInputController mInputController; private final SensorController mSensorController; private final CameraAccessController mCameraAccessController; @Nullable private final ViewConfigurationController mViewConfigurationController; Loading Loading @@ -425,7 +425,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub IBinder token, AttributionSource attributionSource, int deviceId, InputController inputController, VirtualInputController inputController, CameraAccessController cameraAccessController, PendingTrampolineCallback pendingTrampolineCallback, IVirtualDeviceActivityListener activityListener, Loading Loading @@ -479,7 +479,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mBaseVirtualDisplayFlags = flags; if (inputController == null) { mInputController = new InputController(context.getMainThreadHandler(), mInputController = new VirtualInputController( context.getSystemService(InputManager.class), context.getSystemService(WindowManager.class), mAttributionSource); } else { Loading Loading @@ -997,16 +997,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createDpad(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId())); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createDpad(deviceToken, config)); } @Override // Binder call Loading @@ -1016,21 +1008,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { IVirtualInputDevice device = mInputController.createKeyboard( config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId()), config.getLanguageTag(), config.getLayoutType()); IVirtualInputDevice device = Binder.withCleanCallingIdentity(() -> mInputController.createKeyboard(deviceToken, config)); synchronized (mVirtualDeviceLock) { mLocaleList = LocaleList.forLanguageTags(config.getLanguageTag()); } return device; } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } } @Override // Binder call Loading @@ -1040,15 +1023,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createMouse(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, config.getAssociatedDisplayId()); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createMouse(deviceToken, config)); } @Override // Binder call Loading @@ -1058,16 +1034,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createTouchscreen(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, config.getAssociatedDisplayId(), config.getHeight(), config.getWidth()); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createTouchscreen(deviceToken, config)); } @Override // Binder call Loading @@ -1077,17 +1045,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createNavigationTouchpad(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId()), config.getHeight(), config.getWidth()); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createNavigationTouchpad(deviceToken, config)); } @Override // Binder call Loading @@ -1097,16 +1056,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createStylus(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, config.getAssociatedDisplayId(), config.getHeight(), config.getWidth()); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createStylus(deviceToken, config)); } @Override // Binder call Loading @@ -1116,16 +1067,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createRotaryEncoder(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId())); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createRotaryEncoder(deviceToken, config)); } @Override // Binder call Loading Loading @@ -1330,16 +1273,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub indent + "hasCustomAudioInputSupport: " + hasCustomAudioInputSupportInternal()); } // For display mirroring, we want to dispatch all key events to the source (default) display, // as the virtual display doesn't have any focused windows. Hence, call this for // associating any input device to the source display if the input device emits any key events. private int getTargetDisplayIdForInput(int displayId) { DisplayManagerInternal displayManager = LocalServices.getService( DisplayManagerInternal.class); int mirroredDisplayId = displayManager.getDisplayIdToMirror(displayId); return mirroredDisplayId == Display.INVALID_DISPLAY ? displayId : mirroredDisplayId; } private GenericWindowPolicyController createWindowPolicyController( @NonNull Set<String> displayCategories) { final boolean activityLaunchAllowedByDefault = Loading Loading @@ -1725,8 +1658,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } boolean isInputDeviceOwnedByVirtualDevice(int inputDeviceId) { return mInputController.getInputDeviceDescriptors().values().stream().anyMatch( inputDeviceDescriptor -> inputDeviceDescriptor.getInputDeviceId() == inputDeviceId); return mInputController.isInputDevicePresent(inputDeviceId); } void playSoundEffect(int effectType) { Loading services/companion/java/com/android/server/companion/virtual/VirtualInputController.java 0 → 100644 +250 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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; import android.annotation.NonNull; import android.content.AttributionSource; import android.graphics.PointF; import android.hardware.input.IVirtualInputDevice; import android.hardware.input.InputManager; import android.hardware.input.VirtualDpadConfig; import android.hardware.input.VirtualKeyboardConfig; import android.hardware.input.VirtualMouseConfig; import android.hardware.input.VirtualNavigationTouchpadConfig; import android.hardware.input.VirtualRotaryEncoderConfig; import android.hardware.input.VirtualStylusConfig; import android.hardware.input.VirtualTouchscreenConfig; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Slog; import android.view.WindowManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.expresslog.Counter; import com.android.server.LocalServices; import com.android.server.input.InputManagerInternal; import java.io.PrintWriter; import java.util.Iterator; import java.util.Map; /** Controls virtual input devices, including device lifecycle and event dispatch. */ class VirtualInputController { private static final String TAG = "InputController"; private final Object mLock = new Object(); /* Token -> file descriptor associations. */ @GuardedBy("mLock") private final ArrayMap<IBinder, IVirtualInputDevice> mInputDevices = new ArrayMap<>(); private final InputManagerInternal mInputManagerInternal; private final InputManager mInputManager; private final WindowManager mWindowManager; private final AttributionSource mAttributionSource; @VisibleForTesting VirtualInputController(@NonNull InputManager inputManager, @NonNull WindowManager windowManager, AttributionSource attributionSource) { mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mInputManager = inputManager; mWindowManager = windowManager; mAttributionSource = attributionSource; } void close() { synchronized (mLock) { final Iterator<Map.Entry<IBinder, IVirtualInputDevice>> iterator = mInputDevices.entrySet().iterator(); if (iterator.hasNext()) { final Map.Entry<IBinder, IVirtualInputDevice> entry = iterator.next(); final IBinder token = entry.getKey(); iterator.remove(); mInputManagerInternal.closeVirtualInputDevice(token); } } } IVirtualInputDevice createDpad(@NonNull IBinder token, @NonNull VirtualDpadConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualDpad(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_dpad_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createKeyboard(@NonNull IBinder token, @NonNull VirtualKeyboardConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualKeyboard(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_keyboard_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createMouse(@NonNull IBinder token, @NonNull VirtualMouseConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualMouse(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_mouse_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createTouchscreen(@NonNull IBinder token, @NonNull VirtualTouchscreenConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualTouchscreen(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_touchscreen_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createNavigationTouchpad(@NonNull IBinder token, @NonNull VirtualNavigationTouchpadConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualNavigationTouchpad(token, config); Counter.logIncrementWithUid( "virtual_devices.value_virtual_navigationtouchpad_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createStylus(@NonNull IBinder token, @NonNull VirtualStylusConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualStylus(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_stylus_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createRotaryEncoder(@NonNull IBinder token, @NonNull VirtualRotaryEncoderConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualRotaryEncoder(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_rotary_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } void unregisterInputDevice(@NonNull IBinder token) { synchronized (mLock) { final IVirtualInputDevice inputDeviceDescriptor = mInputDevices.remove(token); if (inputDeviceDescriptor == null) { Slog.w(TAG, "Could not unregister input device for given token."); } else { Binder.withCleanCallingIdentity(() -> mInputManagerInternal.closeVirtualInputDevice(token)); } } } /** * @return the device id for a given token (identifiying a device) */ int getInputDeviceId(IBinder token) { synchronized (mLock) { final IVirtualInputDevice inputDeviceDescriptor = mInputDevices.get(token); if (inputDeviceDescriptor == null) { throw new IllegalArgumentException("Could not get device id for given token"); } try { return inputDeviceDescriptor.getInputDeviceId(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } return -1; } void setShowPointerIcon(boolean visible, int displayId) { mInputManagerInternal.setPointerIconVisible(visible, displayId); } void setMouseScalingEnabled(boolean enabled, int displayId) { mInputManager.setMouseScalingEnabled(enabled, displayId); } void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) { mInputManagerInternal.setDisplayEligibilityForPointerCapture(displayId, isEligible); } void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) { mWindowManager.setDisplayImePolicy(displayId, policy); } public PointF getCursorPosition(@NonNull IBinder token) { synchronized (mLock) { final IVirtualInputDevice inputDeviceDescriptor = mInputDevices.get(token); if (inputDeviceDescriptor == null) { throw new IllegalArgumentException( "Could not get cursor position for input device for given token"); } return Binder.withCleanCallingIdentity(() -> { try { return mInputManager.getCursorPosition( inputDeviceDescriptor.getAssociatedDisplayId()); } catch (RemoteException e) { e.rethrowFromSystemServer(); return null; } }); } } public void dump(@NonNull PrintWriter fout) { final String prefix = " "; fout.println(prefix + "InputController: "); synchronized (mLock) { if (mInputDevices.isEmpty()) { fout.println(prefix + prefix + "No active input devices"); } else { for (int i = 0; i < mInputDevices.size(); ++i) { fout.println(prefix + prefix + mInputDevices.valueAt(i).toString()); } } } } @VisibleForTesting void addDeviceForTesting(IBinder token, IVirtualInputDevice device) { synchronized (mLock) { mInputDevices.put(token, device); } } boolean isInputDevicePresent(int inputDeviceId) { synchronized (mLock) { try { for (int i = 0; i < mInputDevices.size(); ++i) { IVirtualInputDevice device = mInputDevices.valueAt(i); if (device.getInputDeviceId() == inputDeviceId) { return true; } } } catch (RemoteException e) { e.rethrowFromSystemServer(); } } return false; } } services/core/java/com/android/server/input/InputManagerInternal.java +92 −0 Original line number Diff line number Diff line Loading @@ -22,7 +22,15 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.hardware.display.DisplayTopologyGraph; import android.hardware.display.DisplayViewport; import android.hardware.input.IVirtualInputDevice; import android.hardware.input.KeyGestureEvent; import android.hardware.input.VirtualDpadConfig; import android.hardware.input.VirtualKeyboardConfig; import android.hardware.input.VirtualMouseConfig; import android.hardware.input.VirtualNavigationTouchpadConfig; import android.hardware.input.VirtualRotaryEncoderConfig; import android.hardware.input.VirtualStylusConfig; import android.hardware.input.VirtualTouchscreenConfig; import android.os.IBinder; import android.os.RemoteException; import android.util.SparseBooleanArray; Loading Loading @@ -363,4 +371,88 @@ public abstract class InputManagerInternal { * trying again. */ public abstract long interceptKeyCombinationBeforeAccessibility(@NonNull KeyEvent event); /** * Creates a new virtual keyboard. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualKeyboard(@NonNull IBinder token, @NonNull VirtualKeyboardConfig config); /** * Creates a new virtual mouse. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualMouse(@NonNull IBinder token, @NonNull VirtualMouseConfig config); /** * Creates a new virtual touchscreen. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualTouchscreen(@NonNull IBinder token, @NonNull VirtualTouchscreenConfig config); /** * Creates a new virtual navigation touchpad. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualNavigationTouchpad(@NonNull IBinder token, @NonNull VirtualNavigationTouchpadConfig config); /** * Creates a new virtual dpad. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualDpad(@NonNull IBinder token, @NonNull VirtualDpadConfig config); /** * Creates a new virtual stylus. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualStylus(@NonNull IBinder token, @NonNull VirtualStylusConfig config); /** * Creates a new virtual rotary encoder. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualRotaryEncoder(@NonNull IBinder token, @NonNull VirtualRotaryEncoderConfig config); /** * Removes an input device with the given id. * * @param token token identifying the device to remove */ public abstract void closeVirtualInputDevice(IBinder token); } services/core/java/com/android/server/input/InputManagerService.java +99 −3 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/java/android/hardware/input/IVirtualInputDevice.aidl +6 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,12 @@ interface IVirtualInputDevice { */ int getInputDeviceId(); /** * Returns the ID of the display that this virtual input device is associated with, or * {@code INVALID_DISPLAY} if not associated with any display. */ int getAssociatedDisplayId(); /** * Injects a virtual dpad key event. */ Loading
services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +21 −89 Original line number Diff line number Diff line Loading @@ -195,7 +195,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final int mDeviceId; @Nullable private final String mPersistentDeviceId; private final InputController mInputController; private final VirtualInputController mInputController; private final SensorController mSensorController; private final CameraAccessController mCameraAccessController; @Nullable private final ViewConfigurationController mViewConfigurationController; Loading Loading @@ -425,7 +425,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub IBinder token, AttributionSource attributionSource, int deviceId, InputController inputController, VirtualInputController inputController, CameraAccessController cameraAccessController, PendingTrampolineCallback pendingTrampolineCallback, IVirtualDeviceActivityListener activityListener, Loading Loading @@ -479,7 +479,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mBaseVirtualDisplayFlags = flags; if (inputController == null) { mInputController = new InputController(context.getMainThreadHandler(), mInputController = new VirtualInputController( context.getSystemService(InputManager.class), context.getSystemService(WindowManager.class), mAttributionSource); } else { Loading Loading @@ -997,16 +997,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createDpad(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId())); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createDpad(deviceToken, config)); } @Override // Binder call Loading @@ -1016,21 +1008,12 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { IVirtualInputDevice device = mInputController.createKeyboard( config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId()), config.getLanguageTag(), config.getLayoutType()); IVirtualInputDevice device = Binder.withCleanCallingIdentity(() -> mInputController.createKeyboard(deviceToken, config)); synchronized (mVirtualDeviceLock) { mLocaleList = LocaleList.forLanguageTags(config.getLanguageTag()); } return device; } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } } @Override // Binder call Loading @@ -1040,15 +1023,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createMouse(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, config.getAssociatedDisplayId()); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createMouse(deviceToken, config)); } @Override // Binder call Loading @@ -1058,16 +1034,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createTouchscreen(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, config.getAssociatedDisplayId(), config.getHeight(), config.getWidth()); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createTouchscreen(deviceToken, config)); } @Override // Binder call Loading @@ -1077,17 +1045,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createNavigationTouchpad(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId()), config.getHeight(), config.getWidth()); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createNavigationTouchpad(deviceToken, config)); } @Override // Binder call Loading @@ -1097,16 +1056,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createStylus(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, config.getAssociatedDisplayId(), config.getHeight(), config.getWidth()); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createStylus(deviceToken, config)); } @Override // Binder call Loading @@ -1116,16 +1067,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub Objects.requireNonNull(config); Objects.requireNonNull(deviceToken); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); final long ident = Binder.clearCallingIdentity(); try { return mInputController.createRotaryEncoder(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId())); } catch (InputController.DeviceCreationException e) { throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } return Binder.withCleanCallingIdentity(() -> mInputController.createRotaryEncoder(deviceToken, config)); } @Override // Binder call Loading Loading @@ -1330,16 +1273,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub indent + "hasCustomAudioInputSupport: " + hasCustomAudioInputSupportInternal()); } // For display mirroring, we want to dispatch all key events to the source (default) display, // as the virtual display doesn't have any focused windows. Hence, call this for // associating any input device to the source display if the input device emits any key events. private int getTargetDisplayIdForInput(int displayId) { DisplayManagerInternal displayManager = LocalServices.getService( DisplayManagerInternal.class); int mirroredDisplayId = displayManager.getDisplayIdToMirror(displayId); return mirroredDisplayId == Display.INVALID_DISPLAY ? displayId : mirroredDisplayId; } private GenericWindowPolicyController createWindowPolicyController( @NonNull Set<String> displayCategories) { final boolean activityLaunchAllowedByDefault = Loading Loading @@ -1725,8 +1658,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } boolean isInputDeviceOwnedByVirtualDevice(int inputDeviceId) { return mInputController.getInputDeviceDescriptors().values().stream().anyMatch( inputDeviceDescriptor -> inputDeviceDescriptor.getInputDeviceId() == inputDeviceId); return mInputController.isInputDevicePresent(inputDeviceId); } void playSoundEffect(int effectType) { Loading
services/companion/java/com/android/server/companion/virtual/VirtualInputController.java 0 → 100644 +250 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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; import android.annotation.NonNull; import android.content.AttributionSource; import android.graphics.PointF; import android.hardware.input.IVirtualInputDevice; import android.hardware.input.InputManager; import android.hardware.input.VirtualDpadConfig; import android.hardware.input.VirtualKeyboardConfig; import android.hardware.input.VirtualMouseConfig; import android.hardware.input.VirtualNavigationTouchpadConfig; import android.hardware.input.VirtualRotaryEncoderConfig; import android.hardware.input.VirtualStylusConfig; import android.hardware.input.VirtualTouchscreenConfig; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Slog; import android.view.WindowManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.modules.expresslog.Counter; import com.android.server.LocalServices; import com.android.server.input.InputManagerInternal; import java.io.PrintWriter; import java.util.Iterator; import java.util.Map; /** Controls virtual input devices, including device lifecycle and event dispatch. */ class VirtualInputController { private static final String TAG = "InputController"; private final Object mLock = new Object(); /* Token -> file descriptor associations. */ @GuardedBy("mLock") private final ArrayMap<IBinder, IVirtualInputDevice> mInputDevices = new ArrayMap<>(); private final InputManagerInternal mInputManagerInternal; private final InputManager mInputManager; private final WindowManager mWindowManager; private final AttributionSource mAttributionSource; @VisibleForTesting VirtualInputController(@NonNull InputManager inputManager, @NonNull WindowManager windowManager, AttributionSource attributionSource) { mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); mInputManager = inputManager; mWindowManager = windowManager; mAttributionSource = attributionSource; } void close() { synchronized (mLock) { final Iterator<Map.Entry<IBinder, IVirtualInputDevice>> iterator = mInputDevices.entrySet().iterator(); if (iterator.hasNext()) { final Map.Entry<IBinder, IVirtualInputDevice> entry = iterator.next(); final IBinder token = entry.getKey(); iterator.remove(); mInputManagerInternal.closeVirtualInputDevice(token); } } } IVirtualInputDevice createDpad(@NonNull IBinder token, @NonNull VirtualDpadConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualDpad(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_dpad_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createKeyboard(@NonNull IBinder token, @NonNull VirtualKeyboardConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualKeyboard(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_keyboard_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createMouse(@NonNull IBinder token, @NonNull VirtualMouseConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualMouse(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_mouse_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createTouchscreen(@NonNull IBinder token, @NonNull VirtualTouchscreenConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualTouchscreen(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_touchscreen_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createNavigationTouchpad(@NonNull IBinder token, @NonNull VirtualNavigationTouchpadConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualNavigationTouchpad(token, config); Counter.logIncrementWithUid( "virtual_devices.value_virtual_navigationtouchpad_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createStylus(@NonNull IBinder token, @NonNull VirtualStylusConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualStylus(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_stylus_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } IVirtualInputDevice createRotaryEncoder(@NonNull IBinder token, @NonNull VirtualRotaryEncoderConfig config) { IVirtualInputDevice device = mInputManagerInternal.createVirtualRotaryEncoder(token, config); Counter.logIncrementWithUid("virtual_devices.value_virtual_rotary_created_count", mAttributionSource.getUid()); mInputDevices.put(token, device); return device; } void unregisterInputDevice(@NonNull IBinder token) { synchronized (mLock) { final IVirtualInputDevice inputDeviceDescriptor = mInputDevices.remove(token); if (inputDeviceDescriptor == null) { Slog.w(TAG, "Could not unregister input device for given token."); } else { Binder.withCleanCallingIdentity(() -> mInputManagerInternal.closeVirtualInputDevice(token)); } } } /** * @return the device id for a given token (identifiying a device) */ int getInputDeviceId(IBinder token) { synchronized (mLock) { final IVirtualInputDevice inputDeviceDescriptor = mInputDevices.get(token); if (inputDeviceDescriptor == null) { throw new IllegalArgumentException("Could not get device id for given token"); } try { return inputDeviceDescriptor.getInputDeviceId(); } catch (RemoteException e) { e.rethrowFromSystemServer(); } } return -1; } void setShowPointerIcon(boolean visible, int displayId) { mInputManagerInternal.setPointerIconVisible(visible, displayId); } void setMouseScalingEnabled(boolean enabled, int displayId) { mInputManager.setMouseScalingEnabled(enabled, displayId); } void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) { mInputManagerInternal.setDisplayEligibilityForPointerCapture(displayId, isEligible); } void setDisplayImePolicy(int displayId, @WindowManager.DisplayImePolicy int policy) { mWindowManager.setDisplayImePolicy(displayId, policy); } public PointF getCursorPosition(@NonNull IBinder token) { synchronized (mLock) { final IVirtualInputDevice inputDeviceDescriptor = mInputDevices.get(token); if (inputDeviceDescriptor == null) { throw new IllegalArgumentException( "Could not get cursor position for input device for given token"); } return Binder.withCleanCallingIdentity(() -> { try { return mInputManager.getCursorPosition( inputDeviceDescriptor.getAssociatedDisplayId()); } catch (RemoteException e) { e.rethrowFromSystemServer(); return null; } }); } } public void dump(@NonNull PrintWriter fout) { final String prefix = " "; fout.println(prefix + "InputController: "); synchronized (mLock) { if (mInputDevices.isEmpty()) { fout.println(prefix + prefix + "No active input devices"); } else { for (int i = 0; i < mInputDevices.size(); ++i) { fout.println(prefix + prefix + mInputDevices.valueAt(i).toString()); } } } } @VisibleForTesting void addDeviceForTesting(IBinder token, IVirtualInputDevice device) { synchronized (mLock) { mInputDevices.put(token, device); } } boolean isInputDevicePresent(int inputDeviceId) { synchronized (mLock) { try { for (int i = 0; i < mInputDevices.size(); ++i) { IVirtualInputDevice device = mInputDevices.valueAt(i); if (device.getInputDeviceId() == inputDeviceId) { return true; } } } catch (RemoteException e) { e.rethrowFromSystemServer(); } } return false; } }
services/core/java/com/android/server/input/InputManagerInternal.java +92 −0 Original line number Diff line number Diff line Loading @@ -22,7 +22,15 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.hardware.display.DisplayTopologyGraph; import android.hardware.display.DisplayViewport; import android.hardware.input.IVirtualInputDevice; import android.hardware.input.KeyGestureEvent; import android.hardware.input.VirtualDpadConfig; import android.hardware.input.VirtualKeyboardConfig; import android.hardware.input.VirtualMouseConfig; import android.hardware.input.VirtualNavigationTouchpadConfig; import android.hardware.input.VirtualRotaryEncoderConfig; import android.hardware.input.VirtualStylusConfig; import android.hardware.input.VirtualTouchscreenConfig; import android.os.IBinder; import android.os.RemoteException; import android.util.SparseBooleanArray; Loading Loading @@ -363,4 +371,88 @@ public abstract class InputManagerInternal { * trying again. */ public abstract long interceptKeyCombinationBeforeAccessibility(@NonNull KeyEvent event); /** * Creates a new virtual keyboard. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualKeyboard(@NonNull IBinder token, @NonNull VirtualKeyboardConfig config); /** * Creates a new virtual mouse. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualMouse(@NonNull IBinder token, @NonNull VirtualMouseConfig config); /** * Creates a new virtual touchscreen. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualTouchscreen(@NonNull IBinder token, @NonNull VirtualTouchscreenConfig config); /** * Creates a new virtual navigation touchpad. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualNavigationTouchpad(@NonNull IBinder token, @NonNull VirtualNavigationTouchpadConfig config); /** * Creates a new virtual dpad. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualDpad(@NonNull IBinder token, @NonNull VirtualDpadConfig config); /** * Creates a new virtual stylus. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualStylus(@NonNull IBinder token, @NonNull VirtualStylusConfig config); /** * Creates a new virtual rotary encoder. * * @param token token identifying the device * @param config the input device configuration * @return the new virtual input device, or {@code null} if the creation failed. */ @NonNull public abstract IVirtualInputDevice createVirtualRotaryEncoder(@NonNull IBinder token, @NonNull VirtualRotaryEncoderConfig config); /** * Removes an input device with the given id. * * @param token token identifying the device to remove */ public abstract void closeVirtualInputDevice(IBinder token); }
services/core/java/com/android/server/input/InputManagerService.java +99 −3 File changed.Preview size limit exceeded, changes collapsed. Show changes