diff options
| author | 2023-11-17 17:32:37 +0000 | |
|---|---|---|
| committer | 2023-11-17 17:32:37 +0000 | |
| commit | 072eeea86110d607834d95e41458c35e79d107d1 (patch) | |
| tree | 3d8652fa0845242724f359057b8ae3701db0bef4 | |
| parent | 8530259b9e57406833e7383a00e2d91508f04140 (diff) | |
| parent | b05edb2f6c5ad9d34340f79f4f6e5921e39e0f62 (diff) | |
Merge "Virtual input API improvements" into main
11 files changed, 263 insertions, 264 deletions
diff --git a/core/java/android/hardware/input/VirtualDpad.java b/core/java/android/hardware/input/VirtualDpad.java index 7f2d8a026a2f..5985c39034ea 100644 --- a/core/java/android/hardware/input/VirtualDpad.java +++ b/core/java/android/hardware/input/VirtualDpad.java @@ -22,6 +22,7 @@ import android.annotation.SystemApi; import android.companion.virtual.IVirtualDevice; import android.os.IBinder; import android.os.RemoteException; +import android.util.Log; import android.view.KeyEvent; import java.util.Arrays; @@ -80,7 +81,10 @@ public class VirtualDpad extends VirtualInputDevice { + event.getKeyCode() + " sent to a VirtualDpad input device."); } - mVirtualDevice.sendDpadKeyEvent(mToken, event); + if (!mVirtualDevice.sendDpadKeyEvent(mToken, event)) { + Log.w(TAG, "Failed to send key event to virtual dpad " + + mConfig.getInputDeviceName()); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/input/VirtualInputDevice.java b/core/java/android/hardware/input/VirtualInputDevice.java index 931e1ff10505..affa4ed7e983 100644 --- a/core/java/android/hardware/input/VirtualInputDevice.java +++ b/core/java/android/hardware/input/VirtualInputDevice.java @@ -20,6 +20,7 @@ import android.annotation.RequiresPermission; import android.companion.virtual.IVirtualDevice; import android.os.IBinder; import android.os.RemoteException; +import android.util.Log; import java.io.Closeable; @@ -32,6 +33,8 @@ import java.io.Closeable; */ abstract class VirtualInputDevice implements Closeable { + protected static final String TAG = "VirtualInputDevice"; + /** * The virtual device to which this VirtualInputDevice belongs to. */ @@ -67,6 +70,7 @@ abstract class VirtualInputDevice implements Closeable { @Override @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close() { + Log.d(TAG, "Closing virtual input device " + mConfig.getInputDeviceName()); try { mVirtualDevice.unregisterInputDevice(mToken); } catch (RemoteException e) { diff --git a/core/java/android/hardware/input/VirtualInputDeviceConfig.java b/core/java/android/hardware/input/VirtualInputDeviceConfig.java index a8caa58ada01..a87980c34f2d 100644 --- a/core/java/android/hardware/input/VirtualInputDeviceConfig.java +++ b/core/java/android/hardware/input/VirtualInputDeviceConfig.java @@ -19,6 +19,10 @@ package android.hardware.input; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.Parcel; +import android.view.Display; + +import java.nio.charset.StandardCharsets; +import java.util.Objects; /** * Common configurations to create virtual input devices. @@ -27,6 +31,15 @@ import android.os.Parcel; */ @SystemApi public abstract class VirtualInputDeviceConfig { + + /** + * The maximum length of a device name (in bytes in UTF-8 encoding). + * + * This limitation comes directly from uinput. + * See also UINPUT_MAX_NAME_SIZE in linux/uinput.h + */ + private static final int DEVICE_NAME_MAX_LENGTH = 80; + /** The vendor id uniquely identifies the company who manufactured the device. */ private final int mVendorId; /** @@ -44,18 +57,33 @@ public abstract class VirtualInputDeviceConfig { mVendorId = builder.mVendorId; mProductId = builder.mProductId; mAssociatedDisplayId = builder.mAssociatedDisplayId; - mInputDeviceName = builder.mInputDeviceName; + mInputDeviceName = Objects.requireNonNull(builder.mInputDeviceName); + + if (mAssociatedDisplayId == Display.INVALID_DISPLAY) { + throw new IllegalArgumentException( + "Display association is required for virtual input devices."); + } + + // Comparison is greater or equal because the device name must fit into a const char* + // including the \0-terminator. Therefore the actual number of bytes that can be used + // for device name is DEVICE_NAME_MAX_LENGTH - 1 + if (mInputDeviceName.getBytes(StandardCharsets.UTF_8).length >= DEVICE_NAME_MAX_LENGTH) { + throw new IllegalArgumentException("Input device name exceeds maximum length of " + + DEVICE_NAME_MAX_LENGTH + "bytes: " + mInputDeviceName); + } } protected VirtualInputDeviceConfig(@NonNull Parcel in) { mVendorId = in.readInt(); mProductId = in.readInt(); mAssociatedDisplayId = in.readInt(); - mInputDeviceName = in.readString8(); + mInputDeviceName = Objects.requireNonNull(in.readString8()); } /** * The vendor id uniquely identifies the company who manufactured the device. + * + * @see Builder#setVendorId(int) (int) */ public int getVendorId() { return mVendorId; @@ -64,6 +92,8 @@ public abstract class VirtualInputDeviceConfig { /** * The product id uniquely identifies which product within the address space of a given vendor, * identified by the device's vendor id. + * + * @see Builder#setProductId(int) */ public int getProductId() { return mProductId; @@ -71,6 +101,8 @@ public abstract class VirtualInputDeviceConfig { /** * The associated display ID of the virtual input device. + * + * @see Builder#setAssociatedDisplayId(int) */ public int getAssociatedDisplayId() { return mAssociatedDisplayId; @@ -78,6 +110,8 @@ public abstract class VirtualInputDeviceConfig { /** * The name of the virtual input device. + * + * @see Builder#setInputDeviceName(String) */ @NonNull public String getInputDeviceName() { @@ -117,11 +151,12 @@ public abstract class VirtualInputDeviceConfig { private int mVendorId; private int mProductId; - private int mAssociatedDisplayId; - @NonNull + private int mAssociatedDisplayId = Display.INVALID_DISPLAY; private String mInputDeviceName; - /** @see VirtualInputDeviceConfig#getVendorId(). */ + /** + * Sets the vendor id of the device, identifying the company who manufactured the device. + */ @NonNull public T setVendorId(int vendorId) { mVendorId = vendorId; @@ -129,24 +164,40 @@ public abstract class VirtualInputDeviceConfig { } - /** @see VirtualInputDeviceConfig#getProductId(). */ + /** + * Sets the product id of the device, uniquely identifying the device within the address + * space of a given vendor, identified by the device's vendor id. + */ @NonNull public T setProductId(int productId) { mProductId = productId; return self(); } - /** @see VirtualInputDeviceConfig#getAssociatedDisplayId(). */ + /** + * Sets the associated display ID of the virtual input device. Required. + * + * <p>The input device is restricted to the display with the given ID and may not send + * events to any other display.</p> + */ @NonNull public T setAssociatedDisplayId(int displayId) { mAssociatedDisplayId = displayId; return self(); } - /** @see VirtualInputDeviceConfig#getInputDeviceName(). */ + /** + * Sets the name of the virtual input device. Required. + * + * <p>The name must be unique among all input devices that belong to the same virtual + * device.</p> + * + * <p>The maximum allowed length of the name is 80 bytes in UTF-8 encoding, enforced by + * {@code UINPUT_MAX_NAME_SIZE}.</p> + */ @NonNull public T setInputDeviceName(@NonNull String deviceName) { - mInputDeviceName = deviceName; + mInputDeviceName = Objects.requireNonNull(deviceName); return self(); } diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java index c90f8932a89d..6eb2ae38ed82 100644 --- a/core/java/android/hardware/input/VirtualKeyboard.java +++ b/core/java/android/hardware/input/VirtualKeyboard.java @@ -22,6 +22,7 @@ import android.annotation.SystemApi; import android.companion.virtual.IVirtualDevice; import android.os.IBinder; import android.os.RemoteException; +import android.util.Log; import android.view.KeyEvent; /** @@ -57,7 +58,10 @@ public class VirtualKeyboard extends VirtualInputDevice { "Unsupported key code " + event.getKeyCode() + " sent to a VirtualKeyboard input device."); } - mVirtualDevice.sendKeyEvent(mToken, event); + if (!mVirtualDevice.sendKeyEvent(mToken, event)) { + Log.w(TAG, "Failed to send key event to virtual keyboard " + + mConfig.getInputDeviceName()); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/input/VirtualMouse.java b/core/java/android/hardware/input/VirtualMouse.java index 51f3f69eb78e..fb0f70049273 100644 --- a/core/java/android/hardware/input/VirtualMouse.java +++ b/core/java/android/hardware/input/VirtualMouse.java @@ -23,6 +23,7 @@ import android.companion.virtual.IVirtualDevice; import android.graphics.PointF; import android.os.IBinder; import android.os.RemoteException; +import android.util.Log; import android.view.MotionEvent; /** @@ -52,7 +53,10 @@ public class VirtualMouse extends VirtualInputDevice { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendButtonEvent(@NonNull VirtualMouseButtonEvent event) { try { - mVirtualDevice.sendButtonEvent(mToken, event); + if (!mVirtualDevice.sendButtonEvent(mToken, event)) { + Log.w(TAG, "Failed to send button event to virtual mouse " + + mConfig.getInputDeviceName()); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -69,7 +73,10 @@ public class VirtualMouse extends VirtualInputDevice { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendScrollEvent(@NonNull VirtualMouseScrollEvent event) { try { - mVirtualDevice.sendScrollEvent(mToken, event); + if (!mVirtualDevice.sendScrollEvent(mToken, event)) { + Log.w(TAG, "Failed to send scroll event to virtual mouse " + + mConfig.getInputDeviceName()); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -85,7 +92,10 @@ public class VirtualMouse extends VirtualInputDevice { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendRelativeEvent(@NonNull VirtualMouseRelativeEvent event) { try { - mVirtualDevice.sendRelativeEvent(mToken, event); + if (!mVirtualDevice.sendRelativeEvent(mToken, event)) { + Log.w(TAG, "Failed to send relative event to virtual mouse " + + mConfig.getInputDeviceName()); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/input/VirtualNavigationTouchpad.java b/core/java/android/hardware/input/VirtualNavigationTouchpad.java index 61d72e2fd554..3dbb38568f68 100644 --- a/core/java/android/hardware/input/VirtualNavigationTouchpad.java +++ b/core/java/android/hardware/input/VirtualNavigationTouchpad.java @@ -22,6 +22,7 @@ import android.annotation.SystemApi; import android.companion.virtual.IVirtualDevice; import android.os.IBinder; import android.os.RemoteException; +import android.util.Log; /** * A virtual navigation touchpad representing a touch-based input mechanism on a remote device. @@ -53,7 +54,10 @@ public class VirtualNavigationTouchpad extends VirtualInputDevice { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendTouchEvent(@NonNull VirtualTouchEvent event) { try { - mVirtualDevice.sendTouchEvent(mToken, event); + if (!mVirtualDevice.sendTouchEvent(mToken, event)) { + Log.w(TAG, "Failed to send touch event to virtual navigation touchpad " + + mConfig.getInputDeviceName()); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/input/VirtualTouchscreen.java b/core/java/android/hardware/input/VirtualTouchscreen.java index 4ac439e0eff1..2c800aadef37 100644 --- a/core/java/android/hardware/input/VirtualTouchscreen.java +++ b/core/java/android/hardware/input/VirtualTouchscreen.java @@ -22,6 +22,7 @@ import android.annotation.SystemApi; import android.companion.virtual.IVirtualDevice; import android.os.IBinder; import android.os.RemoteException; +import android.util.Log; /** * A virtual touchscreen representing a touch-based display input mechanism on a remote device. @@ -47,7 +48,10 @@ public class VirtualTouchscreen extends VirtualInputDevice { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendTouchEvent(@NonNull VirtualTouchEvent event) { try { - mVirtualDevice.sendTouchEvent(mToken, event); + if (!mVirtualDevice.sendTouchEvent(mToken, event)) { + Log.w(TAG, "Failed to send touch event to virtual touchscreen " + + mConfig.getInputDeviceName()); + } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java index eeaa423b1aef..74415b5a8a87 100644 --- a/services/companion/java/com/android/server/companion/virtual/InputController.java +++ b/services/companion/java/com/android/server/companion/virtual/InputController.java @@ -49,7 +49,6 @@ import com.android.server.input.InputManagerInternal; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.nio.charset.StandardCharsets; import java.util.Iterator; import java.util.Map; import java.util.Objects; @@ -83,14 +82,6 @@ class InputController { @interface PhysType { } - /** - * The maximum length of a device name (in bytes in UTF-8 encoding). - * - * This limitation comes directly from uinput. - * See also UINPUT_MAX_NAME_SIZE in linux/uinput.h - */ - private static final int DEVICE_NAME_MAX_LENGTH = 80; - final Object mLock = new Object(); /* Token -> file descriptor associations. */ @@ -138,25 +129,17 @@ class InputController { } } - void createDpad(@NonNull String deviceName, - int vendorId, - int productId, - @NonNull IBinder deviceToken, - int displayId) { + void createDpad(@NonNull String deviceName, int vendorId, int productId, + @NonNull IBinder deviceToken, int displayId) throws DeviceCreationException { final String phys = createPhys(PHYS_TYPE_DPAD); - try { - createDeviceInternal(InputDeviceDescriptor.TYPE_DPAD, deviceName, vendorId, + createDeviceInternal(InputDeviceDescriptor.TYPE_DPAD, deviceName, vendorId, productId, deviceToken, displayId, phys, () -> mNativeWrapper.openUinputDpad(deviceName, vendorId, productId, phys)); - } catch (DeviceCreationException e) { - throw new RuntimeException( - "Failed to create virtual dpad device '" + deviceName + "'.", e); - } } void createKeyboard(@NonNull String deviceName, int vendorId, int productId, @NonNull IBinder deviceToken, int displayId, @NonNull String languageTag, - @NonNull String layoutType) { + @NonNull String layoutType) throws DeviceCreationException { final String phys = createPhys(PHYS_TYPE_KEYBOARD); mInputManagerInternal.addKeyboardLayoutAssociation(phys, languageTag, layoutType); @@ -166,66 +149,42 @@ class InputController { () -> mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys)); } catch (DeviceCreationException e) { mInputManagerInternal.removeKeyboardLayoutAssociation(phys); - throw new RuntimeException( - "Failed to create virtual keyboard device '" + deviceName + "'.", e); + throw e; } } - void createMouse(@NonNull String deviceName, - int vendorId, - int productId, - @NonNull IBinder deviceToken, - int displayId) { + void createMouse(@NonNull String deviceName, int vendorId, int productId, + @NonNull IBinder deviceToken, int displayId) throws DeviceCreationException { final String phys = createPhys(PHYS_TYPE_MOUSE); - try { - createDeviceInternal(InputDeviceDescriptor.TYPE_MOUSE, deviceName, vendorId, productId, - deviceToken, displayId, phys, - () -> mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys)); - } catch (DeviceCreationException e) { - throw new RuntimeException( - "Failed to create virtual mouse device: '" + deviceName + "'.", e); - } + createDeviceInternal(InputDeviceDescriptor.TYPE_MOUSE, deviceName, vendorId, productId, + deviceToken, displayId, phys, + () -> mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys)); mInputManagerInternal.setVirtualMousePointerDisplayId(displayId); } - void createTouchscreen(@NonNull String deviceName, - int vendorId, - int productId, - @NonNull IBinder deviceToken, - int displayId, - int height, - int width) { + void createTouchscreen(@NonNull String deviceName, int vendorId, int productId, + @NonNull IBinder deviceToken, int displayId, int height, int width) + throws DeviceCreationException { final String phys = createPhys(PHYS_TYPE_TOUCHSCREEN); - try { - createDeviceInternal(InputDeviceDescriptor.TYPE_TOUCHSCREEN, deviceName, vendorId, - productId, deviceToken, displayId, phys, - () -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, - phys, height, width)); - } catch (DeviceCreationException e) { - throw new RuntimeException( - "Failed to create virtual touchscreen device '" + deviceName + "'.", e); - } + createDeviceInternal(InputDeviceDescriptor.TYPE_TOUCHSCREEN, deviceName, vendorId, + productId, deviceToken, displayId, phys, + () -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, phys, + height, width)); } - void createNavigationTouchpad( - @NonNull String deviceName, - int vendorId, - int productId, - @NonNull IBinder deviceToken, - int displayId, - int touchpadHeight, - int touchpadWidth) { + void createNavigationTouchpad(@NonNull String deviceName, int vendorId, int productId, + @NonNull IBinder deviceToken, int displayId, int height, int width) + throws DeviceCreationException { final String phys = createPhys(PHYS_TYPE_NAVIGATION_TOUCHPAD); mInputManagerInternal.setTypeAssociation(phys, NAVIGATION_TOUCHPAD_DEVICE_TYPE); try { createDeviceInternal(InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD, deviceName, vendorId, productId, deviceToken, displayId, phys, () -> mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, - phys, touchpadHeight, touchpadWidth)); + phys, height, width)); } catch (DeviceCreationException e) { mInputManagerInternal.unsetTypeAssociation(phys); - throw new RuntimeException( - "Failed to create virtual navigation touchpad device '" + deviceName + "'.", e); + throw e; } } @@ -234,10 +193,10 @@ class InputController { final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.remove( token); if (inputDeviceDescriptor == null) { - throw new IllegalArgumentException( - "Could not unregister input device for given token"); + Slog.w(TAG, "Could not unregister input device for given token."); + } else { + closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor); } - closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor); } } @@ -326,21 +285,11 @@ class InputController { } /** - * Validates a device name by checking length and whether a device with the same name - * already exists. Throws exceptions if the validation fails. + * Validates a device name by checking whether a device with the same name already exists. * @param deviceName The name of the device to be validated * @throws DeviceCreationException if {@code deviceName} is not valid. */ private void validateDeviceName(String deviceName) throws DeviceCreationException { - // Comparison is greater or equal because the device name must fit into a const char* - // including the \0-terminator. Therefore the actual number of bytes that can be used - // for device name is DEVICE_NAME_MAX_LENGTH - 1 - if (deviceName.getBytes(StandardCharsets.UTF_8).length >= DEVICE_NAME_MAX_LENGTH) { - throw new DeviceCreationException( - "Input device name exceeds maximum length of " + DEVICE_NAME_MAX_LENGTH - + "bytes: " + deviceName); - } - synchronized (mLock) { for (int i = 0; i < mInputDeviceDescriptors.size(); ++i) { if (mInputDeviceDescriptors.valueAt(i).mName.equals(deviceName)) { @@ -365,8 +314,7 @@ class InputController { final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( token); if (inputDeviceDescriptor == null) { - throw new IllegalArgumentException( - "Could not send key event to input device for given token"); + return false; } return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getNativePointer(), event.getKeyCode(), event.getAction(), event.getEventTimeNanos()); @@ -378,8 +326,7 @@ class InputController { final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( token); if (inputDeviceDescriptor == null) { - throw new IllegalArgumentException( - "Could not send key event to input device for given token"); + return false; } return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getNativePointer(), event.getKeyCode(), event.getAction(), event.getEventTimeNanos()); @@ -391,8 +338,7 @@ class InputController { final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( token); if (inputDeviceDescriptor == null) { - throw new IllegalArgumentException( - "Could not send button event to input device for given token"); + return false; } if (inputDeviceDescriptor.getDisplayId() != mInputManagerInternal.getVirtualMousePointerDisplayId()) { @@ -409,8 +355,7 @@ class InputController { final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( token); if (inputDeviceDescriptor == null) { - throw new IllegalArgumentException( - "Could not send touch event to input device for given token"); + return false; } return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getNativePointer(), event.getPointerId(), event.getToolType(), event.getAction(), event.getX(), @@ -424,8 +369,7 @@ class InputController { final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( token); if (inputDeviceDescriptor == null) { - throw new IllegalArgumentException( - "Could not send relative event to input device for given token"); + return false; } if (inputDeviceDescriptor.getDisplayId() != mInputManagerInternal.getVirtualMousePointerDisplayId()) { @@ -442,8 +386,7 @@ class InputController { final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( token); if (inputDeviceDescriptor == null) { - throw new IllegalArgumentException( - "Could not send scroll event to input device for given token"); + return false; } if (inputDeviceDescriptor.getDisplayId() != mInputManagerInternal.getVirtualMousePointerDisplayId()) { @@ -758,13 +701,19 @@ class InputController { } /** An internal exception that is thrown to indicate an error when opening a virtual device. */ - private static class DeviceCreationException extends Exception { + static class DeviceCreationException extends Exception { + DeviceCreationException() { + super(); + } DeviceCreationException(String message) { super(message); } - DeviceCreationException(String message, Exception cause) { + DeviceCreationException(String message, Throwable cause) { super(message, cause); } + DeviceCreationException(Throwable cause) { + super(cause); + } } /** diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 118943df1bf6..e6bfeb79fafb 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -694,6 +694,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mInputController.createDpad(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId())); + } catch (InputController.DeviceCreationException e) { + throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } @@ -705,15 +707,17 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub super.createVirtualKeyboard_enforcePermission(); Objects.requireNonNull(config); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); - synchronized (mVirtualDeviceLock) { - mLocaleList = LocaleList.forLanguageTags(config.getLanguageTag()); - } final long ident = Binder.clearCallingIdentity(); try { mInputController.createKeyboard(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId()), config.getLanguageTag(), config.getLayoutType()); + synchronized (mVirtualDeviceLock) { + mLocaleList = LocaleList.forLanguageTags(config.getLanguageTag()); + } + } catch (InputController.DeviceCreationException e) { + throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } @@ -729,6 +733,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub try { mInputController.createMouse(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, config.getAssociatedDisplayId()); + } catch (InputController.DeviceCreationException e) { + throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } @@ -741,19 +747,13 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub super.createVirtualTouchscreen_enforcePermission(); Objects.requireNonNull(config); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); - int screenHeight = config.getHeight(); - int screenWidth = config.getWidth(); - if (screenHeight <= 0 || screenWidth <= 0) { - throw new IllegalArgumentException( - "Cannot create a virtual touchscreen, screen dimensions must be positive. Got: " - + "(" + screenWidth + ", " + screenHeight + ")"); - } - final long ident = Binder.clearCallingIdentity(); try { mInputController.createTouchscreen(config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, config.getAssociatedDisplayId(), - screenHeight, screenWidth); + config.getHeight(), config.getWidth()); + } catch (InputController.DeviceCreationException e) { + throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } @@ -766,21 +766,15 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub super.createVirtualNavigationTouchpad_enforcePermission(); Objects.requireNonNull(config); checkVirtualInputDeviceDisplayIdAssociation(config.getAssociatedDisplayId()); - int touchpadHeight = config.getHeight(); - int touchpadWidth = config.getWidth(); - if (touchpadHeight <= 0 || touchpadWidth <= 0) { - throw new IllegalArgumentException( - "Cannot create a virtual navigation touchpad, touchpad dimensions must be positive." - + " Got: (" + touchpadHeight + ", " + touchpadWidth + ")"); - } - final long ident = Binder.clearCallingIdentity(); try { mInputController.createNavigationTouchpad( config.getInputDeviceName(), config.getVendorId(), config.getProductId(), deviceToken, getTargetDisplayIdForInput(config.getAssociatedDisplayId()), - touchpadHeight, touchpadWidth); + config.getHeight(), config.getWidth()); + } catch (InputController.DeviceCreationException e) { + throw new IllegalArgumentException(e); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java index 7e6883bcec63..ccbbaa52ac21 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java @@ -100,7 +100,7 @@ public class InputControllerTest { } @Test - public void registerInputDevice_deviceCreation_hasDeviceId() { + public void registerInputDevice_deviceCreation_hasDeviceId() throws Exception { final IBinder device1Token = new Binder("device1"); mInputController.createMouse("mouse", /*vendorId= */ 1, /*productId= */ 1, device1Token, /* displayId= */ 1); @@ -124,7 +124,7 @@ public class InputControllerTest { } @Test - public void unregisterInputDevice_allMiceUnregistered_clearPointerDisplayId() { + public void unregisterInputDevice_allMiceUnregistered_clearPointerDisplayId() throws Exception { final IBinder deviceToken = new Binder(); mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken, /* displayId= */ 1); @@ -137,7 +137,8 @@ public class InputControllerTest { } @Test - public void unregisterInputDevice_anotherMouseExists_setPointerDisplayIdOverride() { + public void unregisterInputDevice_anotherMouseExists_setPointerDisplayIdOverride() + throws Exception { final IBinder deviceToken = new Binder(); mInputController.createMouse("mouse1", /*vendorId= */ 1, /*productId= */ 1, deviceToken, /* displayId= */ 1); @@ -153,7 +154,7 @@ public class InputControllerTest { } @Test - public void createNavigationTouchpad_hasDeviceId() { + public void createNavigationTouchpad_hasDeviceId() throws Exception { final IBinder deviceToken = new Binder(); mInputController.createNavigationTouchpad("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken, /* displayId= */ 1, /* touchpadHeight= */ 50, /* touchpadWidth= */ 50); @@ -166,7 +167,7 @@ public class InputControllerTest { } @Test - public void createNavigationTouchpad_setsTypeAssociation() { + public void createNavigationTouchpad_setsTypeAssociation() throws Exception { final IBinder deviceToken = new Binder(); mInputController.createNavigationTouchpad("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken, /* displayId= */ 1, /* touchpadHeight= */ 50, /* touchpadWidth= */ 50); @@ -176,7 +177,7 @@ public class InputControllerTest { } @Test - public void createAndUnregisterNavigationTouchpad_unsetsTypeAssociation() { + public void createAndUnregisterNavigationTouchpad_unsetsTypeAssociation() throws Exception { final IBinder deviceToken = new Binder(); mInputController.createNavigationTouchpad("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken, /* displayId= */ 1, /* touchpadHeight= */ 50, /* touchpadWidth= */ 50); @@ -188,7 +189,7 @@ public class InputControllerTest { } @Test - public void createKeyboard_addAndRemoveKeyboardLayoutAssociation() { + public void createKeyboard_addAndRemoveKeyboardLayoutAssociation() throws Exception { final IBinder deviceToken = new Binder("device"); mInputController.createKeyboard("keyboard", /*vendorId= */2, /*productId= */ 2, deviceToken, @@ -201,56 +202,7 @@ public class InputControllerTest { } @Test - public void createInputDevice_tooLongNameRaisesException() { - final IBinder deviceToken = new Binder("device"); - // The underlying uinput implementation only supports device names up to 80 bytes. This - // string is all ASCII characters, therefore if we have more than 80 ASCII characters we - // will have more than 80 bytes. - String deviceName = - "This.is.a.very.long.device.name.that.exceeds.the.maximum.length.of.80.bytes" - + ".by.a.couple.bytes"; - - assertThrows(RuntimeException.class, () -> { - mInputController.createDpad(deviceName, /*vendorId= */3, /*productId=*/3, deviceToken, - 1); - }); - } - - @Test - public void createInputDevice_tooLongDeviceNameRaisesException() { - final IBinder deviceToken = new Binder("device"); - // The underlying uinput implementation only supports device names up to 80 bytes (including - // a 0-byte terminator). - // This string is 79 characters and 80 bytes (including the 0-byte terminator) - String deviceName = - "This.is.a.very.long.device.name.that.exceeds.the.maximum.length01234567890123456"; - - assertThrows(RuntimeException.class, () -> { - mInputController.createDpad(deviceName, /*vendorId= */3, /*productId=*/3, deviceToken, - 1); - }); - } - - @Test - public void createInputDevice_stringWithLessThanMaxCharsButMoreThanMaxBytesRaisesException() { - final IBinder deviceToken = new Binder("device1"); - - // Has only 39 characters but is 109 bytes as utf-8 - String device_name = - "░▄▄▄▄░\n" + - "▀▀▄██►\n" + - "▀▀███►\n" + - "░▀███►░█►\n" + - "▒▄████▀▀"; - - assertThrows(RuntimeException.class, () -> { - mInputController.createDpad(device_name, /*vendorId= */5, /*productId=*/5, - deviceToken, 1); - }); - } - - @Test - public void createInputDevice_duplicateNamesAreNotAllowed() { + public void createInputDevice_duplicateNamesAreNotAllowed() throws Exception { final IBinder deviceToken1 = new Binder("deviceToken1"); final IBinder deviceToken2 = new Binder("deviceToken2"); @@ -258,9 +210,9 @@ public class InputControllerTest { mInputController.createDpad(sharedDeviceName, /*vendorId= */4, /*productId=*/4, deviceToken1, 1); - assertThrows("Device names need to be unique", RuntimeException.class, () -> { - mInputController.createDpad(sharedDeviceName, /*vendorId= */5, /*productId=*/5, - deviceToken2, 2); - }); + assertThrows("Device names need to be unique", + InputController.DeviceCreationException.class, + () -> mInputController.createDpad( + sharedDeviceName, /*vendorId= */5, /*productId=*/5, deviceToken2, 2)); } } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index 30300ec3ad2e..0c857bc18d16 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -33,6 +33,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; @@ -368,6 +369,18 @@ public class VirtualDeviceManagerServiceTest { new Handler(TestableLooper.get(this).getLooper())); when(mContext.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager); + when(mNativeWrapperMock.writeButtonEvent(anyLong(), anyInt(), anyInt(), anyLong())) + .thenReturn(true); + when(mNativeWrapperMock.writeRelativeEvent(anyLong(), anyFloat(), anyFloat(), anyLong())) + .thenReturn(true); + when(mNativeWrapperMock.writeScrollEvent(anyLong(), anyFloat(), anyFloat(), anyLong())) + .thenReturn(true); + when(mNativeWrapperMock.writeKeyEvent(anyLong(), anyInt(), anyInt(), anyLong())) + .thenReturn(true); + when(mNativeWrapperMock.writeTouchEvent(anyLong(), anyInt(), anyInt(), anyInt(), + anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong())) + .thenReturn(true); + mInputManagerMockHelper = new InputManagerMockHelper( TestableLooper.get(this), mNativeWrapperMock, mIInputManagerMock); // Allow virtual devices to be created on the looper thread for testing. @@ -1183,12 +1196,12 @@ public class VirtualDeviceManagerServiceTest { @Test public void sendKeyEvent_noFd() { - assertThrows( - IllegalArgumentException.class, - () -> - mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder() - .setKeyCode(KeyEvent.KEYCODE_A) - .setAction(VirtualKeyEvent.ACTION_DOWN).build())); + assertThat(mDeviceImpl.sendKeyEvent(BINDER, + new VirtualKeyEvent.Builder() + .setKeyCode(KeyEvent.KEYCODE_A) + .setAction(VirtualKeyEvent.ACTION_DOWN) + .build())) + .isFalse(); } @Test @@ -1201,24 +1214,24 @@ public class VirtualDeviceManagerServiceTest { InputController.InputDeviceDescriptor.TYPE_KEYBOARD, DISPLAY_ID_1, PHYS, DEVICE_NAME_1, INPUT_DEVICE_ID); - mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder() - .setKeyCode(keyCode) - .setAction(action) - .setEventTimeNanos(eventTimeNanos) - .build()); + assertThat(mDeviceImpl.sendKeyEvent(BINDER, + new VirtualKeyEvent.Builder() + .setKeyCode(keyCode) + .setAction(action) + .setEventTimeNanos(eventTimeNanos) + .build())) + .isTrue(); verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action, eventTimeNanos); } @Test public void sendButtonEvent_noFd() { - assertThrows( - IllegalArgumentException.class, - () -> - mDeviceImpl.sendButtonEvent(BINDER, - new VirtualMouseButtonEvent.Builder() - .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK) - .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS) - .build())); + assertThat(mDeviceImpl.sendButtonEvent(BINDER, + new VirtualMouseButtonEvent.Builder() + .setButtonCode(VirtualMouseButtonEvent.BUTTON_BACK) + .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS) + .build())) + .isFalse(); } @Test @@ -1231,11 +1244,13 @@ public class VirtualDeviceManagerServiceTest { InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1, INPUT_DEVICE_ID); doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); - mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder() - .setButtonCode(buttonCode) - .setAction(action) - .setEventTimeNanos(eventTimeNanos) - .build()); + assertThat(mDeviceImpl.sendButtonEvent(BINDER, + new VirtualMouseButtonEvent.Builder() + .setButtonCode(buttonCode) + .setAction(action) + .setEventTimeNanos(eventTimeNanos) + .build())) + .isTrue(); verify(mNativeWrapperMock).writeButtonEvent(fd, buttonCode, action, eventTimeNanos); } @@ -1257,12 +1272,12 @@ public class VirtualDeviceManagerServiceTest { @Test public void sendRelativeEvent_noFd() { - assertThrows( - IllegalArgumentException.class, - () -> - mDeviceImpl.sendRelativeEvent(BINDER, - new VirtualMouseRelativeEvent.Builder().setRelativeX( - 0.0f).setRelativeY(0.0f).build())); + assertThat(mDeviceImpl.sendRelativeEvent(BINDER, + new VirtualMouseRelativeEvent.Builder() + .setRelativeX(0.0f) + .setRelativeY(0.0f) + .build())) + .isFalse(); } @Test @@ -1275,14 +1290,17 @@ public class VirtualDeviceManagerServiceTest { InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1, INPUT_DEVICE_ID); doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); - mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder() - .setRelativeX(x) - .setRelativeY(y) - .setEventTimeNanos(eventTimeNanos) - .build()); + assertThat(mDeviceImpl.sendRelativeEvent(BINDER, + new VirtualMouseRelativeEvent.Builder() + .setRelativeX(x) + .setRelativeY(y) + .setEventTimeNanos(eventTimeNanos) + .build())) + .isTrue(); verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y, eventTimeNanos); } + @Test public void sendRelativeEvent_hasFd_wrongDisplay_throwsIllegalStateException() { final int fd = 1; @@ -1301,13 +1319,12 @@ public class VirtualDeviceManagerServiceTest { @Test public void sendScrollEvent_noFd() { - assertThrows( - IllegalArgumentException.class, - () -> - mDeviceImpl.sendScrollEvent(BINDER, - new VirtualMouseScrollEvent.Builder() - .setXAxisMovement(-1f) - .setYAxisMovement(1f).build())); + assertThat(mDeviceImpl.sendScrollEvent(BINDER, + new VirtualMouseScrollEvent.Builder() + .setXAxisMovement(-1f) + .setYAxisMovement(1f) + .build())) + .isFalse(); } @Test @@ -1320,14 +1337,17 @@ public class VirtualDeviceManagerServiceTest { InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1, INPUT_DEVICE_ID); doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId(); - mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder() - .setXAxisMovement(x) - .setYAxisMovement(y) - .setEventTimeNanos(eventTimeNanos) - .build()); + assertThat(mDeviceImpl.sendScrollEvent(BINDER, + new VirtualMouseScrollEvent.Builder() + .setXAxisMovement(x) + .setYAxisMovement(y) + .setEventTimeNanos(eventTimeNanos) + .build())) + .isTrue(); verify(mNativeWrapperMock).writeScrollEvent(fd, x, y, eventTimeNanos); } + @Test public void sendScrollEvent_hasFd_wrongDisplay_throwsIllegalStateException() { final int fd = 1; @@ -1346,16 +1366,15 @@ public class VirtualDeviceManagerServiceTest { @Test public void sendTouchEvent_noFd() { - assertThrows( - IllegalArgumentException.class, - () -> - mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder() - .setX(0.0f) - .setY(0.0f) - .setAction(VirtualTouchEvent.ACTION_UP) - .setPointerId(1) - .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER) - .build())); + assertThat(mDeviceImpl.sendTouchEvent(BINDER, + new VirtualTouchEvent.Builder() + .setX(0.0f) + .setY(0.0f) + .setAction(VirtualTouchEvent.ACTION_UP) + .setPointerId(1) + .setToolType(VirtualTouchEvent.TOOL_TYPE_FINGER) + .build())) + .isFalse(); } @Test @@ -1370,14 +1389,16 @@ public class VirtualDeviceManagerServiceTest { mInputController.addDeviceForTesting(BINDER, fd, InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS, DEVICE_NAME_1, INPUT_DEVICE_ID); - mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder() - .setX(x) - .setY(y) - .setAction(action) - .setPointerId(pointerId) - .setToolType(toolType) - .setEventTimeNanos(eventTimeNanos) - .build()); + assertThat(mDeviceImpl.sendTouchEvent(BINDER, + new VirtualTouchEvent.Builder() + .setX(x) + .setY(y) + .setAction(action) + .setPointerId(pointerId) + .setToolType(toolType) + .setEventTimeNanos(eventTimeNanos) + .build())) + .isTrue(); verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN, Float.NaN, eventTimeNanos); } @@ -1396,16 +1417,18 @@ public class VirtualDeviceManagerServiceTest { mInputController.addDeviceForTesting(BINDER, fd, InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS, DEVICE_NAME_1, INPUT_DEVICE_ID); - mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder() - .setX(x) - .setY(y) - .setAction(action) - .setPointerId(pointerId) - .setToolType(toolType) - .setPressure(pressure) - .setMajorAxisSize(majorAxisSize) - .setEventTimeNanos(eventTimeNanos) - .build()); + assertThat(mDeviceImpl.sendTouchEvent(BINDER, + new VirtualTouchEvent.Builder() + .setX(x) + .setY(y) + .setAction(action) + .setPointerId(pointerId) + .setToolType(toolType) + .setPressure(pressure) + .setMajorAxisSize(majorAxisSize) + .setEventTimeNanos(eventTimeNanos) + .build())) + .isTrue(); verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure, majorAxisSize, eventTimeNanos); } |