Loading core/java/android/companion/virtual/IVirtualDevice.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -88,6 +88,7 @@ interface IVirtualDevice { IBinder token, in Point screenSize); void unregisterInputDevice(IBinder token); int getInputDeviceId(IBinder token); boolean sendDpadKeyEvent(IBinder token, in VirtualKeyEvent event); boolean sendKeyEvent(IBinder token, in VirtualKeyEvent event); boolean sendButtonEvent(IBinder token, in VirtualMouseButtonEvent event); Loading core/java/android/hardware/input/VirtualInputDevice.java +11 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,17 @@ abstract class VirtualInputDevice implements Closeable { mToken = token; } /** * @return The device id of this device. * @hide */ public int getInputDeviceId() { try { return mVirtualDevice.getInputDeviceId(mToken); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) Loading services/companion/java/com/android/server/companion/virtual/InputController.java +57 −6 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.hardware.input.VirtualMouseScrollEvent; import android.hardware.input.VirtualTouchEvent; import android.os.Handler; import android.os.IBinder; import android.os.IInputConstants; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Slog; Loading Loading @@ -75,7 +76,7 @@ class InputController { @interface PhysType { } private final Object mLock; final Object mLock; /* Token -> file descriptor associations. */ @VisibleForTesting Loading Loading @@ -220,6 +221,19 @@ class InputController { } } /** * @return the device id for a given token (identifiying a device) */ int getInputDeviceId(IBinder token) { synchronized (mLock) { final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(token); if (inputDeviceDescriptor == null) { throw new IllegalArgumentException("Could not get device id for given token"); } return inputDeviceDescriptor.getInputDeviceId(); } } void setShowPointerIcon(boolean visible, int displayId) { mInputManagerInternal.setPointerIconVisible(visible, displayId); } Loading Loading @@ -393,8 +407,20 @@ class InputController { + inputDeviceDescriptor.getCreationOrderNumber()); fout.println(" type: " + inputDeviceDescriptor.getType()); fout.println(" phys: " + inputDeviceDescriptor.getPhys()); fout.println( " inputDeviceId: " + inputDeviceDescriptor.getInputDeviceId()); } } } @VisibleForTesting void addDeviceForTesting(IBinder deviceToken, int fd, int type, int displayId, String phys, int inputDeviceId) { synchronized (mLock) { mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(fd, () -> {}, type, displayId, phys, inputDeviceId)); } } private static native int nativeOpenUinputDpad(String deviceName, int vendorId, Loading Loading @@ -493,16 +519,20 @@ class InputController { private final @Type int mType; private final int mDisplayId; private final String mPhys; // The input device id that was associated to the device by the InputReader on device // creation. private final int mInputDeviceId; // Monotonically increasing number; devices with lower numbers were created earlier. private final long mCreationOrderNumber; InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type, int displayId, String phys) { int displayId, String phys, int inputDeviceId) { mFd = fd; mDeathRecipient = deathRecipient; mType = type; mDisplayId = displayId; mPhys = phys; mInputDeviceId = inputDeviceId; mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement(); } Loading Loading @@ -533,6 +563,10 @@ class InputController { public String getPhys() { return mPhys; } public int getInputDeviceId() { return mInputDeviceId; } } private final class BinderDeathRecipient implements IBinder.DeathRecipient { Loading @@ -558,6 +592,8 @@ class InputController { private final CountDownLatch mDeviceAddedLatch = new CountDownLatch(1); private final InputManager.InputDeviceListener mListener; private int mInputDeviceId = IInputConstants.INVALID_INPUT_DEVICE_ID; WaitForDevice(String deviceName, int vendorId, int productId) { mListener = new InputManager.InputDeviceListener() { @Override Loading @@ -572,6 +608,7 @@ class InputController { if (id.getVendorId() != vendorId || id.getProductId() != productId) { return; } mInputDeviceId = deviceId; mDeviceAddedLatch.countDown(); } Loading @@ -588,8 +625,13 @@ class InputController { InputManager.getInstance().registerInputDeviceListener(mListener, mHandler); } /** Note: This must not be called from {@link #mHandler}'s thread. */ void waitForDeviceCreation() throws DeviceCreationException { /** * Note: This must not be called from {@link #mHandler}'s thread. * @throws DeviceCreationException if the device was not created successfully within the * timeout. * @return The id of the created input device. */ int waitForDeviceCreation() throws DeviceCreationException { try { if (!mDeviceAddedLatch.await(1, TimeUnit.MINUTES)) { throw new DeviceCreationException( Loading @@ -599,6 +641,12 @@ class InputController { throw new DeviceCreationException( "Interrupted while waiting for virtual device to be created.", e); } if (mInputDeviceId == IInputConstants.INVALID_INPUT_DEVICE_ID) { throw new IllegalStateException( "Virtual input device was created with an invalid " + "id=" + mInputDeviceId); } return mInputDeviceId; } @Override Loading Loading @@ -643,6 +691,8 @@ class InputController { final int fd; final BinderDeathRecipient binderDeathRecipient; final int inputDeviceId; setUniqueIdAssociation(displayId, phys); try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId)) { fd = deviceOpener.get(); Loading @@ -652,7 +702,7 @@ class InputController { } // The fd is valid from here, so ensure that all failures close the fd after this point. try { waiter.waitForDeviceCreation(); inputDeviceId = waiter.waitForDeviceCreation(); binderDeathRecipient = new BinderDeathRecipient(deviceToken); try { Loading @@ -672,7 +722,8 @@ class InputController { synchronized (mLock) { mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys)); new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys, inputDeviceId)); } } Loading services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +11 −0 Original line number Diff line number Diff line Loading @@ -497,6 +497,17 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } @Override // Binder call public int getInputDeviceId(IBinder token) { final long binderToken = Binder.clearCallingIdentity(); try { return mInputController.getInputDeviceId(token); } finally { Binder.restoreCallingIdentity(binderToken); } } @Override // Binder call public boolean sendDpadKeyEvent(IBinder token, VirtualKeyEvent event) { final long binderToken = Binder.clearCallingIdentity(); Loading services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java +28 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.companion.virtual; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; Loading @@ -25,6 +27,7 @@ import static org.mockito.Mockito.verify; import android.hardware.display.DisplayManagerInternal; import android.hardware.input.IInputManager; import android.hardware.input.InputManager; import android.os.Binder; import android.os.Handler; import android.os.IBinder; Loading Loading @@ -87,6 +90,30 @@ public class InputControllerTest { threadVerifier); } @Test public void registerInputDevice_deviceCreation_hasDeviceId() { final IBinder device1Token = new Binder("device1"); mInputController.createMouse("mouse", /*vendorId= */ 1, /*productId= */ 1, device1Token, /* displayId= */ 1); int device1Id = mInputController.getInputDeviceId(device1Token); final IBinder device2Token = new Binder("device2"); mInputController.createKeyboard("keyboard", /*vendorId= */2, /*productId= */ 2, device2Token, 2); int device2Id = mInputController.getInputDeviceId(device2Token); assertWithMessage("Different devices should have different id").that( device1Id).isNotEqualTo(device2Id); int[] deviceIds = InputManager.getInstance().getInputDeviceIds(); assertWithMessage("InputManager's deviceIds list should contain id of device 1").that( deviceIds).asList().contains(device1Id); assertWithMessage("InputManager's deviceIds list should contain id of device 2").that( deviceIds).asList().contains(device2Id); } @Test public void unregisterInputDevice_allMiceUnregistered_clearPointerDisplayId() { final IBinder deviceToken = new Binder(); Loading Loading @@ -115,4 +142,5 @@ public class InputControllerTest { mInputController.unregisterInputDevice(deviceToken); verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1)); } } Loading
core/java/android/companion/virtual/IVirtualDevice.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -88,6 +88,7 @@ interface IVirtualDevice { IBinder token, in Point screenSize); void unregisterInputDevice(IBinder token); int getInputDeviceId(IBinder token); boolean sendDpadKeyEvent(IBinder token, in VirtualKeyEvent event); boolean sendKeyEvent(IBinder token, in VirtualKeyEvent event); boolean sendButtonEvent(IBinder token, in VirtualMouseButtonEvent event); Loading
core/java/android/hardware/input/VirtualInputDevice.java +11 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,17 @@ abstract class VirtualInputDevice implements Closeable { mToken = token; } /** * @return The device id of this device. * @hide */ public int getInputDeviceId() { try { return mVirtualDevice.getInputDeviceId(mToken); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } @Override @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) Loading
services/companion/java/com/android/server/companion/virtual/InputController.java +57 −6 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ import android.hardware.input.VirtualMouseScrollEvent; import android.hardware.input.VirtualTouchEvent; import android.os.Handler; import android.os.IBinder; import android.os.IInputConstants; import android.os.RemoteException; import android.util.ArrayMap; import android.util.Slog; Loading Loading @@ -75,7 +76,7 @@ class InputController { @interface PhysType { } private final Object mLock; final Object mLock; /* Token -> file descriptor associations. */ @VisibleForTesting Loading Loading @@ -220,6 +221,19 @@ class InputController { } } /** * @return the device id for a given token (identifiying a device) */ int getInputDeviceId(IBinder token) { synchronized (mLock) { final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(token); if (inputDeviceDescriptor == null) { throw new IllegalArgumentException("Could not get device id for given token"); } return inputDeviceDescriptor.getInputDeviceId(); } } void setShowPointerIcon(boolean visible, int displayId) { mInputManagerInternal.setPointerIconVisible(visible, displayId); } Loading Loading @@ -393,8 +407,20 @@ class InputController { + inputDeviceDescriptor.getCreationOrderNumber()); fout.println(" type: " + inputDeviceDescriptor.getType()); fout.println(" phys: " + inputDeviceDescriptor.getPhys()); fout.println( " inputDeviceId: " + inputDeviceDescriptor.getInputDeviceId()); } } } @VisibleForTesting void addDeviceForTesting(IBinder deviceToken, int fd, int type, int displayId, String phys, int inputDeviceId) { synchronized (mLock) { mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(fd, () -> {}, type, displayId, phys, inputDeviceId)); } } private static native int nativeOpenUinputDpad(String deviceName, int vendorId, Loading Loading @@ -493,16 +519,20 @@ class InputController { private final @Type int mType; private final int mDisplayId; private final String mPhys; // The input device id that was associated to the device by the InputReader on device // creation. private final int mInputDeviceId; // Monotonically increasing number; devices with lower numbers were created earlier. private final long mCreationOrderNumber; InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type, int displayId, String phys) { int displayId, String phys, int inputDeviceId) { mFd = fd; mDeathRecipient = deathRecipient; mType = type; mDisplayId = displayId; mPhys = phys; mInputDeviceId = inputDeviceId; mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement(); } Loading Loading @@ -533,6 +563,10 @@ class InputController { public String getPhys() { return mPhys; } public int getInputDeviceId() { return mInputDeviceId; } } private final class BinderDeathRecipient implements IBinder.DeathRecipient { Loading @@ -558,6 +592,8 @@ class InputController { private final CountDownLatch mDeviceAddedLatch = new CountDownLatch(1); private final InputManager.InputDeviceListener mListener; private int mInputDeviceId = IInputConstants.INVALID_INPUT_DEVICE_ID; WaitForDevice(String deviceName, int vendorId, int productId) { mListener = new InputManager.InputDeviceListener() { @Override Loading @@ -572,6 +608,7 @@ class InputController { if (id.getVendorId() != vendorId || id.getProductId() != productId) { return; } mInputDeviceId = deviceId; mDeviceAddedLatch.countDown(); } Loading @@ -588,8 +625,13 @@ class InputController { InputManager.getInstance().registerInputDeviceListener(mListener, mHandler); } /** Note: This must not be called from {@link #mHandler}'s thread. */ void waitForDeviceCreation() throws DeviceCreationException { /** * Note: This must not be called from {@link #mHandler}'s thread. * @throws DeviceCreationException if the device was not created successfully within the * timeout. * @return The id of the created input device. */ int waitForDeviceCreation() throws DeviceCreationException { try { if (!mDeviceAddedLatch.await(1, TimeUnit.MINUTES)) { throw new DeviceCreationException( Loading @@ -599,6 +641,12 @@ class InputController { throw new DeviceCreationException( "Interrupted while waiting for virtual device to be created.", e); } if (mInputDeviceId == IInputConstants.INVALID_INPUT_DEVICE_ID) { throw new IllegalStateException( "Virtual input device was created with an invalid " + "id=" + mInputDeviceId); } return mInputDeviceId; } @Override Loading Loading @@ -643,6 +691,8 @@ class InputController { final int fd; final BinderDeathRecipient binderDeathRecipient; final int inputDeviceId; setUniqueIdAssociation(displayId, phys); try (WaitForDevice waiter = new WaitForDevice(deviceName, vendorId, productId)) { fd = deviceOpener.get(); Loading @@ -652,7 +702,7 @@ class InputController { } // The fd is valid from here, so ensure that all failures close the fd after this point. try { waiter.waitForDeviceCreation(); inputDeviceId = waiter.waitForDeviceCreation(); binderDeathRecipient = new BinderDeathRecipient(deviceToken); try { Loading @@ -672,7 +722,8 @@ class InputController { synchronized (mLock) { mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys)); new InputDeviceDescriptor(fd, binderDeathRecipient, type, displayId, phys, inputDeviceId)); } } Loading
services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +11 −0 Original line number Diff line number Diff line Loading @@ -497,6 +497,17 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } @Override // Binder call public int getInputDeviceId(IBinder token) { final long binderToken = Binder.clearCallingIdentity(); try { return mInputController.getInputDeviceId(token); } finally { Binder.restoreCallingIdentity(binderToken); } } @Override // Binder call public boolean sendDpadKeyEvent(IBinder token, VirtualKeyEvent event) { final long binderToken = Binder.clearCallingIdentity(); Loading
services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java +28 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.companion.virtual; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; Loading @@ -25,6 +27,7 @@ import static org.mockito.Mockito.verify; import android.hardware.display.DisplayManagerInternal; import android.hardware.input.IInputManager; import android.hardware.input.InputManager; import android.os.Binder; import android.os.Handler; import android.os.IBinder; Loading Loading @@ -87,6 +90,30 @@ public class InputControllerTest { threadVerifier); } @Test public void registerInputDevice_deviceCreation_hasDeviceId() { final IBinder device1Token = new Binder("device1"); mInputController.createMouse("mouse", /*vendorId= */ 1, /*productId= */ 1, device1Token, /* displayId= */ 1); int device1Id = mInputController.getInputDeviceId(device1Token); final IBinder device2Token = new Binder("device2"); mInputController.createKeyboard("keyboard", /*vendorId= */2, /*productId= */ 2, device2Token, 2); int device2Id = mInputController.getInputDeviceId(device2Token); assertWithMessage("Different devices should have different id").that( device1Id).isNotEqualTo(device2Id); int[] deviceIds = InputManager.getInstance().getInputDeviceIds(); assertWithMessage("InputManager's deviceIds list should contain id of device 1").that( deviceIds).asList().contains(device1Id); assertWithMessage("InputManager's deviceIds list should contain id of device 2").that( deviceIds).asList().contains(device2Id); } @Test public void unregisterInputDevice_allMiceUnregistered_clearPointerDisplayId() { final IBinder deviceToken = new Binder(); Loading Loading @@ -115,4 +142,5 @@ public class InputControllerTest { mInputController.unregisterInputDevice(deviceToken); verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1)); } }