From 060ded78412d07a1d237aa4fcdaa58a55fdfb279 Mon Sep 17 00:00:00 2001 From: Phill Hayers Date: Thu, 8 Sep 2022 15:01:06 +0000 Subject: [PATCH] Add a virtual Dpad. Bug: 244519097 Test: atest VirtualDpadTest and atest VirtualDeviceManagerServiceTest Change-Id: Iafce6fccee9f761e79e8fd845bb6e9b1e1490b26 --- core/api/system-current.txt | 6 ++ .../companion/virtual/IVirtualDevice.aidl | 7 ++ .../virtual/VirtualDeviceManager.java | 27 ++++++ .../android/hardware/input/VirtualDpad.java | 94 +++++++++++++++++++ .../hardware/input/VirtualKeyEvent.java | 1 + .../hardware/input/VirtualKeyboard.java | 7 ++ .../companion/virtual/InputController.java | 44 +++++++++ .../companion/virtual/VirtualDeviceImpl.java | 37 ++++++++ ...rver_companion_virtual_InputController.cpp | 42 ++++++++- .../virtual/InputManagerMockHelper.java | 2 + .../VirtualDeviceManagerServiceTest.java | 34 ++++++- 11 files changed, 294 insertions(+), 7 deletions(-) create mode 100644 core/java/android/hardware/input/VirtualDpad.java diff --git a/core/api/system-current.txt b/core/api/system-current.txt index f09244ad7859d..0195fcebad0ff 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2803,6 +2803,7 @@ package android.companion.virtual { method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback); method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); + method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualDpad createVirtualDpad(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); @@ -4372,6 +4373,11 @@ package android.hardware.hdmi { package android.hardware.input { + public class VirtualDpad implements java.io.Closeable { + method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); + method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent); + } + public final class VirtualKeyEvent implements android.os.Parcelable { method public int describeContents(); method public int getAction(); diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl index 8cfbf2faee676..9c99da52d9627 100644 --- a/core/java/android/companion/virtual/IVirtualDevice.aidl +++ b/core/java/android/companion/virtual/IVirtualDevice.aidl @@ -57,6 +57,12 @@ interface IVirtualDevice { void onAudioSessionEnded(); + void createVirtualDpad( + int displayId, + String inputDeviceName, + int vendorId, + int productId, + IBinder token); void createVirtualKeyboard( int displayId, String inputDeviceName, @@ -77,6 +83,7 @@ interface IVirtualDevice { IBinder token, in Point screenSize); void unregisterInputDevice(IBinder token); + boolean sendDpadKeyEvent(IBinder token, in VirtualKeyEvent event); boolean sendKeyEvent(IBinder token, in VirtualKeyEvent event); boolean sendButtonEvent(IBinder token, in VirtualMouseButtonEvent event); boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event); diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 1b93bb851567e..d4c9a42c8115c 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -37,6 +37,7 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.IVirtualDisplayCallback; import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplayConfig; +import android.hardware.input.VirtualDpad; import android.hardware.input.VirtualKeyboard; import android.hardware.input.VirtualMouse; import android.hardware.input.VirtualTouchscreen; @@ -314,6 +315,32 @@ public final class VirtualDeviceManager { } } + /** + * Creates a virtual dpad. + * + * @param display the display that the events inputted through this device should target + * @param inputDeviceName the name to call this input device + * @param vendorId the PCI vendor id + * @param productId the product id, as defined by the vendor + */ + @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) + @NonNull + public VirtualDpad createVirtualDpad( + @NonNull VirtualDisplay display, + @NonNull String inputDeviceName, + int vendorId, + int productId) { + try { + final IBinder token = new Binder( + "android.hardware.input.VirtualDpad:" + inputDeviceName); + mVirtualDevice.createVirtualDpad(display.getDisplay().getDisplayId(), + inputDeviceName, vendorId, productId, token); + return new VirtualDpad(mVirtualDevice, token); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Creates a virtual keyboard. * diff --git a/core/java/android/hardware/input/VirtualDpad.java b/core/java/android/hardware/input/VirtualDpad.java new file mode 100644 index 0000000000000..d7cda9ec33cff --- /dev/null +++ b/core/java/android/hardware/input/VirtualDpad.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.input; + +import android.annotation.NonNull; +import android.annotation.RequiresPermission; +import android.annotation.SystemApi; +import android.companion.virtual.IVirtualDevice; +import android.os.IBinder; +import android.os.RemoteException; +import android.view.KeyEvent; + +import java.io.Closeable; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * A virtual dpad representing a key input mechanism on a remote device. + * + * This registers an InputDevice that is interpreted like a physically-connected device and + * dispatches received events to it. + * + * @hide + */ +@SystemApi +public class VirtualDpad implements Closeable { + + private final Set mSupportedKeyCodes = + Collections.unmodifiableSet( + new HashSet<>( + Arrays.asList( + KeyEvent.KEYCODE_DPAD_UP, + KeyEvent.KEYCODE_DPAD_DOWN, + KeyEvent.KEYCODE_DPAD_LEFT, + KeyEvent.KEYCODE_DPAD_RIGHT, + KeyEvent.KEYCODE_DPAD_CENTER))); + private final IVirtualDevice mVirtualDevice; + private final IBinder mToken; + + /** @hide */ + public VirtualDpad(IVirtualDevice virtualDevice, IBinder token) { + mVirtualDevice = virtualDevice; + mToken = token; + } + + @Override + @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) + public void close() { + try { + mVirtualDevice.unregisterInputDevice(mToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Sends a key event to the system. + * + * Supported key codes are KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT, + * KEYCODE_DPAD_RIGHT and KEYCODE_DPAD_CENTER, + * + * @param event the event to send + */ + @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) + public void sendKeyEvent(@NonNull VirtualKeyEvent event) { + try { + if (!mSupportedKeyCodes.contains(event.getKeyCode())) { + throw new IllegalArgumentException( + "Unsupported key code " + + event.getKeyCode() + + " sent to a VirtualDpad input device."); + } + mVirtualDevice.sendDpadKeyEvent(mToken, event); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } +} diff --git a/core/java/android/hardware/input/VirtualKeyEvent.java b/core/java/android/hardware/input/VirtualKeyEvent.java index 80a49c9252087..dc30e55e36fc2 100644 --- a/core/java/android/hardware/input/VirtualKeyEvent.java +++ b/core/java/android/hardware/input/VirtualKeyEvent.java @@ -158,6 +158,7 @@ public final class VirtualKeyEvent implements Parcelable { KeyEvent.KEYCODE_DPAD_UP, KeyEvent.KEYCODE_DPAD_LEFT, KeyEvent.KEYCODE_DPAD_RIGHT, + KeyEvent.KEYCODE_DPAD_CENTER, KeyEvent.KEYCODE_MOVE_END, KeyEvent.KEYCODE_MOVE_HOME, KeyEvent.KEYCODE_PAGE_DOWN, diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java index ee9b659e95218..901401fea32cd 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.view.KeyEvent; import java.io.Closeable; @@ -37,6 +38,7 @@ import java.io.Closeable; @SystemApi public class VirtualKeyboard implements Closeable { + private final int mUnsupportedKeyCode = KeyEvent.KEYCODE_DPAD_CENTER; private final IVirtualDevice mVirtualDevice; private final IBinder mToken; @@ -64,6 +66,11 @@ public class VirtualKeyboard implements Closeable { @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendKeyEvent(@NonNull VirtualKeyEvent event) { try { + if (mUnsupportedKeyCode == event.getKeyCode()) { + throw new IllegalArgumentException( + "Unsupported key code " + event.getKeyCode() + + " sent to a VirtualKeyboard input device."); + } mVirtualDevice.sendKeyEvent(mToken, event); } 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 05e85e367be8f..838cbd9079d69 100644 --- a/services/companion/java/com/android/server/companion/virtual/InputController.java +++ b/services/companion/java/com/android/server/companion/virtual/InputController.java @@ -61,10 +61,12 @@ class InputController { private static final AtomicLong sNextPhysId = new AtomicLong(1); + static final String PHYS_TYPE_DPAD = "Dpad"; static final String PHYS_TYPE_KEYBOARD = "Keyboard"; static final String PHYS_TYPE_MOUSE = "Mouse"; static final String PHYS_TYPE_TOUCHSCREEN = "Touchscreen"; @StringDef(prefix = { "PHYS_TYPE_" }, value = { + PHYS_TYPE_DPAD, PHYS_TYPE_KEYBOARD, PHYS_TYPE_MOUSE, PHYS_TYPE_TOUCHSCREEN, @@ -121,6 +123,22 @@ class InputController { } } + void createDpad(@NonNull String deviceName, + int vendorId, + int productId, + @NonNull IBinder deviceToken, + int displayId) { + final String phys = createPhys(PHYS_TYPE_DPAD); + try { + 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, @@ -253,6 +271,19 @@ class InputController { InputManager.getInstance().addUniqueIdAssociation(phys, displayUniqueId); } + boolean sendDpadKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) { + synchronized (mLock) { + final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( + token); + if (inputDeviceDescriptor == null) { + throw new IllegalArgumentException( + "Could not send key event to input device for given token"); + } + return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getFileDescriptor(), + event.getKeyCode(), event.getAction()); + } + } + boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) { synchronized (mLock) { final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( @@ -366,6 +397,8 @@ class InputController { } } + private static native int nativeOpenUinputDpad(String deviceName, int vendorId, + int productId, String phys); private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId, int productId, String phys); private static native int nativeOpenUinputMouse(String deviceName, int vendorId, int productId, @@ -373,6 +406,7 @@ class InputController { private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId, int productId, String phys, int height, int width); private static native boolean nativeCloseUinput(int fd); + private static native boolean nativeWriteDpadKeyEvent(int fd, int androidKeyCode, int action); private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action); private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action); private static native boolean nativeWriteTouchEvent(int fd, int pointerId, int toolType, @@ -385,6 +419,10 @@ class InputController { /** Wrapper around the static native methods for tests. */ @VisibleForTesting protected static class NativeWrapper { + public int openUinputDpad(String deviceName, int vendorId, int productId, String phys) { + return nativeOpenUinputDpad(deviceName, vendorId, productId, phys); + } + public int openUinputKeyboard(String deviceName, int vendorId, int productId, String phys) { return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys); } @@ -403,6 +441,10 @@ class InputController { return nativeCloseUinput(fd); } + public boolean writeDpadKeyEvent(int fd, int androidKeyCode, int action) { + return nativeWriteDpadKeyEvent(fd, androidKeyCode, action); + } + public boolean writeKeyEvent(int fd, int androidKeyCode, int action) { return nativeWriteKeyEvent(fd, androidKeyCode, action); } @@ -433,10 +475,12 @@ class InputController { static final int TYPE_KEYBOARD = 1; static final int TYPE_MOUSE = 2; static final int TYPE_TOUCHSCREEN = 3; + static final int TYPE_DPAD = 4; @IntDef(prefix = { "TYPE_" }, value = { TYPE_KEYBOARD, TYPE_MOUSE, TYPE_TOUCHSCREEN, + TYPE_DPAD, }) @Retention(RetentionPolicy.SOURCE) @interface Type { 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 cca3212703f06..4204162f3d988 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -344,6 +344,33 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } + @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) + @Override // Binder call + public void createVirtualDpad( + int displayId, + @NonNull String deviceName, + int vendorId, + int productId, + @NonNull IBinder deviceToken) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CREATE_VIRTUAL_DEVICE, + "Permission required to create a virtual dpad"); + synchronized (mVirtualDeviceLock) { + if (!mVirtualDisplayIds.contains(displayId)) { + throw new SecurityException( + "Cannot create a virtual dpad for a display not associated with " + + "this virtual device"); + } + } + final long token = Binder.clearCallingIdentity(); + try { + mInputController.createDpad(deviceName, vendorId, productId, deviceToken, + displayId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + @Override // Binder call public void createVirtualKeyboard( int displayId, @@ -436,6 +463,16 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } } + @Override // Binder call + public boolean sendDpadKeyEvent(IBinder token, VirtualKeyEvent event) { + final long binderToken = Binder.clearCallingIdentity(); + try { + return mInputController.sendDpadKeyEvent(token, event); + } finally { + Binder.restoreCallingIdentity(binderToken); + } + } + @Override // Binder call public boolean sendKeyEvent(IBinder token, VirtualKeyEvent event) { final long binderToken = Binder.clearCallingIdentity(); diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp index daca1531d41ff..b7a4fd1be2616 100644 --- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp +++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp @@ -37,6 +37,7 @@ enum class DeviceType { KEYBOARD, MOUSE, TOUCHSCREEN, + DPAD, }; enum class UinputAction { @@ -76,6 +77,13 @@ static std::map TOOL_TYPE_MAPPING = { {AMOTION_EVENT_TOOL_TYPE_PALM, MT_TOOL_PALM}, }; +// Dpad keycode mapping from https://source.android.com/devices/input/keyboard-devices +static std::map DPAD_KEY_CODE_MAPPING = { + {AKEYCODE_DPAD_DOWN, KEY_DOWN}, {AKEYCODE_DPAD_UP, KEY_UP}, + {AKEYCODE_DPAD_LEFT, KEY_LEFT}, {AKEYCODE_DPAD_RIGHT, KEY_RIGHT}, + {AKEYCODE_DPAD_CENTER, KEY_SELECT}, +}; + // Keycode mapping from https://source.android.com/devices/input/keyboard-devices static std::map KEY_CODE_MAPPING = { {AKEYCODE_0, KEY_0}, @@ -200,8 +208,13 @@ static int openUinput(const char* readableName, jint vendorId, jint productId, c ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_SYN); switch (deviceType) { + case DeviceType::DPAD: + for (const auto& [_, keyCode] : DPAD_KEY_CODE_MAPPING) { + ioctl(fd, UI_SET_KEYBIT, keyCode); + } + break; case DeviceType::KEYBOARD: - for (const auto& [ignored, keyCode] : KEY_CODE_MAPPING) { + for (const auto& [_, keyCode] : KEY_CODE_MAPPING) { ioctl(fd, UI_SET_KEYBIT, keyCode); } break; @@ -327,6 +340,12 @@ static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productI screenHeight, screenWidth); } +static int nativeOpenUinputDpad(JNIEnv* env, jobject thiz, jstring name, jint vendorId, + jint productId, jstring phys) { + return openUinputJni(env, name, vendorId, productId, phys, DeviceType::DPAD, + /* screenHeight */ 0, /* screenWidth */ 0); +} + static int nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId, jint productId, jstring phys) { return openUinputJni(env, name, vendorId, productId, phys, DeviceType::KEYBOARD, @@ -355,10 +374,10 @@ static bool writeInputEvent(int fd, uint16_t type, uint16_t code, int32_t value) return TEMP_FAILURE_RETRY(write(fd, &ev, sizeof(struct input_event))) == sizeof(ev); } -static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode, - jint action) { - auto keyCodeIterator = KEY_CODE_MAPPING.find(androidKeyCode); - if (keyCodeIterator == KEY_CODE_MAPPING.end()) { +static bool writeKeyEvent(jint fd, jint androidKeyCode, jint action, + const std::map& keyCodeMapping) { + auto keyCodeIterator = keyCodeMapping.find(androidKeyCode); + if (keyCodeIterator == keyCodeMapping.end()) { ALOGE("No supportive native keycode for androidKeyCode %d", androidKeyCode); return false; } @@ -376,6 +395,16 @@ static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint android return true; } +static bool nativeWriteDpadKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode, + jint action) { + return writeKeyEvent(fd, androidKeyCode, action, DPAD_KEY_CODE_MAPPING); +} + +static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode, + jint action) { + return writeKeyEvent(fd, androidKeyCode, action, KEY_CODE_MAPPING); +} + static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jint fd, jint buttonCode, jint action) { auto buttonCodeIterator = BUTTON_CODE_MAPPING.find(buttonCode); @@ -461,6 +490,8 @@ static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jint fd, jfloat xA } static JNINativeMethod methods[] = { + {"nativeOpenUinputDpad", "(Ljava/lang/String;IILjava/lang/String;)I", + (void*)nativeOpenUinputDpad}, {"nativeOpenUinputKeyboard", "(Ljava/lang/String;IILjava/lang/String;)I", (void*)nativeOpenUinputKeyboard}, {"nativeOpenUinputMouse", "(Ljava/lang/String;IILjava/lang/String;)I", @@ -468,6 +499,7 @@ static JNINativeMethod methods[] = { {"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)I", (void*)nativeOpenUinputTouchscreen}, {"nativeCloseUinput", "(I)Z", (void*)nativeCloseUinput}, + {"nativeWriteDpadKeyEvent", "(III)Z", (void*)nativeWriteDpadKeyEvent}, {"nativeWriteKeyEvent", "(III)Z", (void*)nativeWriteKeyEvent}, {"nativeWriteButtonEvent", "(III)Z", (void*)nativeWriteButtonEvent}, {"nativeWriteTouchEvent", "(IIIIFFFF)Z", (void*)nativeWriteTouchEvent}, diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java index cc5ed92e02d90..51bd5b0f76c93 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java @@ -57,6 +57,8 @@ class InputManagerMockHelper { doAnswer(this::handleNativeOpenInputDevice).when(mNativeWrapperMock).openUinputMouse( anyString(), anyInt(), anyInt(), anyString()); + doAnswer(this::handleNativeOpenInputDevice).when(mNativeWrapperMock).openUinputDpad( + anyString(), anyInt(), anyInt(), anyString()); doAnswer(this::handleNativeOpenInputDevice).when(mNativeWrapperMock).openUinputKeyboard( anyString(), anyInt(), anyInt(), anyString()); doAnswer(this::handleNativeOpenInputDevice).when(mNativeWrapperMock).openUinputTouchscreen( 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 cc2cdba69af6d..ef203d03fd98a 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 @@ -386,6 +386,14 @@ public class VirtualDeviceManagerServiceTest { verify(mIPowerManagerMock).releaseWakeLock(eq(wakeLock), anyInt()); } + @Test + public void createVirtualDpad_noDisplay_failsSecurityException() { + assertThrows( + SecurityException.class, + () -> mDeviceImpl.createVirtualDpad(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, + PRODUCT_ID, BINDER)); + } + @Test public void createVirtualKeyboard_noDisplay_failsSecurityException() { assertThrows( @@ -417,6 +425,17 @@ public class VirtualDeviceManagerServiceTest { DISPLAY_ID, mRoutingCallback, mConfigChangedCallback)); } + @Test + public void createVirtualDpad_noPermission_failsSecurityException() { + mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); + doCallRealMethod().when(mContext).enforceCallingOrSelfPermission( + eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString()); + assertThrows( + SecurityException.class, + () -> mDeviceImpl.createVirtualDpad(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, + PRODUCT_ID, BINDER)); + } + @Test public void createVirtualKeyboard_noPermission_failsSecurityException() { mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); @@ -467,6 +486,17 @@ public class VirtualDeviceManagerServiceTest { assertThrows(SecurityException.class, () -> mDeviceImpl.onAudioSessionEnded()); } + @Test + public void createVirtualDpad_hasDisplay_obtainFileDescriptor() { + mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); + mDeviceImpl.createVirtualDpad(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, + BINDER); + assertWithMessage("Virtual dpad should register fd when the display matches") + .that(mInputController.mInputDeviceDescriptors).isNotEmpty(); + verify(mNativeWrapperMock).openUinputDpad(eq(DEVICE_NAME), eq(VENDOR_ID), + eq(PRODUCT_ID), anyString()); + } + @Test public void createVirtualKeyboard_hasDisplay_obtainFileDescriptor() { mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); @@ -483,7 +513,7 @@ public class VirtualDeviceManagerServiceTest { mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER); - assertWithMessage("Virtual keyboard should register fd when the display matches") + assertWithMessage("Virtual mouse should register fd when the display matches") .that(mInputController.mInputDeviceDescriptors).isNotEmpty(); verify(mNativeWrapperMock).openUinputMouse(eq(DEVICE_NAME), eq(VENDOR_ID), eq(PRODUCT_ID), anyString()); @@ -494,7 +524,7 @@ public class VirtualDeviceManagerServiceTest { mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID); mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER, new Point(WIDTH, HEIGHT)); - assertWithMessage("Virtual keyboard should register fd when the display matches") + assertWithMessage("Virtual touchscreen should register fd when the display matches") .that(mInputController.mInputDeviceDescriptors).isNotEmpty(); verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME), eq(VENDOR_ID), eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH));