Merge "Add a virtual Dpad."

This commit is contained in:
Phill Hayers
2022-09-20 12:17:08 +00:00
committed by Android (Google) Code Review
11 changed files with 294 additions and 7 deletions

View File

@@ -2804,6 +2804,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);
@@ -4373,6 +4374,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();

View File

@@ -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);

View File

@@ -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.
*

View File

@@ -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<Integer> 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();
}
}
}

View File

@@ -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,

View File

@@ -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();

View File

@@ -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 {

View File

@@ -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();

View File

@@ -37,6 +37,7 @@ enum class DeviceType {
KEYBOARD,
MOUSE,
TOUCHSCREEN,
DPAD,
};
enum class UinputAction {
@@ -76,6 +77,13 @@ static std::map<int, int> 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<int, int> 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<int, int> 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<int, int>& 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},

View File

@@ -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(

View File

@@ -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));