Update uniqueId association methods
Change from associating via name to associating via physical input port. This is more robust as there is less likely to be a chance of collision, as well as less likely to have problems as the userspace language changes (and therefore the user-visible name should also change). Bug: 202273865 Test: atest FrameworksServicesTests:com.android.server.companion.virtual Change-Id: Ieae349e20f66df1490351c85a99d2efa5910d08b
This commit is contained in:
@@ -122,9 +122,9 @@ interface IInputManager {
|
||||
void removePortAssociation(in String inputPort);
|
||||
|
||||
// Add a runtime association between the input device and display.
|
||||
void addUniqueIdAssociation(in String inputDeviceName, in String displayUniqueId);
|
||||
void addUniqueIdAssociation(in String inputPort, in String displayUniqueId);
|
||||
// Remove the runtime association between the input device and display.
|
||||
void removeUniqueIdAssociation(in String inputDeviceName);
|
||||
void removeUniqueIdAssociation(in String inputPort);
|
||||
|
||||
InputSensorInfo[] getSensorList(int deviceId);
|
||||
|
||||
|
||||
@@ -1359,19 +1359,18 @@ public final class InputManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a runtime association between the input device name and display, by unique id. Input
|
||||
* device names are expected to be unique.
|
||||
* @param inputDeviceName The name of the input device.
|
||||
* Add a runtime association between the input port and display, by unique id. Input ports are
|
||||
* expected to be unique.
|
||||
* @param inputPort The port of the input device.
|
||||
* @param displayUniqueId The unique id of the associated display.
|
||||
* <p>
|
||||
* Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
|
||||
* </p>
|
||||
* @hide
|
||||
*/
|
||||
public void addUniqueIdAssociation(@NonNull String inputDeviceName,
|
||||
@NonNull String displayUniqueId) {
|
||||
public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) {
|
||||
try {
|
||||
mIm.addUniqueIdAssociation(inputDeviceName, displayUniqueId);
|
||||
mIm.addUniqueIdAssociation(inputPort, displayUniqueId);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
@@ -1379,15 +1378,15 @@ public final class InputManager {
|
||||
|
||||
/**
|
||||
* Removes a runtime association between the input device and display.
|
||||
* @param inputDeviceName The name of the input device.
|
||||
* @param inputPort The port of the input device.
|
||||
* <p>
|
||||
* Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}.
|
||||
* </p>
|
||||
* @hide
|
||||
*/
|
||||
public void removeUniqueIdAssociation(@NonNull String inputDeviceName) {
|
||||
public void removeUniqueIdAssociation(@NonNull String inputPort) {
|
||||
try {
|
||||
mIm.removeUniqueIdAssociation(inputDeviceName);
|
||||
mIm.removeUniqueIdAssociation(inputPort);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
|
||||
@@ -18,8 +18,11 @@ package com.android.server.companion.virtual;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.StringDef;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.hardware.display.DisplayManagerInternal;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.hardware.input.InputManagerInternal;
|
||||
import android.hardware.input.VirtualKeyEvent;
|
||||
import android.hardware.input.VirtualMouseButtonEvent;
|
||||
@@ -48,6 +51,20 @@ class InputController {
|
||||
|
||||
private static final String TAG = "VirtualInputController";
|
||||
|
||||
private static final AtomicLong sNextPhysId = new AtomicLong(1);
|
||||
|
||||
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_KEYBOARD,
|
||||
PHYS_TYPE_MOUSE,
|
||||
PHYS_TYPE_TOUCHSCREEN,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface PhysType {
|
||||
}
|
||||
|
||||
private final Object mLock;
|
||||
|
||||
/* Token -> file descriptor associations. */
|
||||
@@ -56,6 +73,8 @@ class InputController {
|
||||
final Map<IBinder, InputDeviceDescriptor> mInputDeviceDescriptors = new ArrayMap<>();
|
||||
|
||||
private final NativeWrapper mNativeWrapper;
|
||||
private final DisplayManagerInternal mDisplayManagerInternal;
|
||||
private final InputManagerInternal mInputManagerInternal;
|
||||
|
||||
/**
|
||||
* Because the pointer is a singleton, it can only be targeted at one display at a time. Because
|
||||
@@ -73,6 +92,8 @@ class InputController {
|
||||
mLock = lock;
|
||||
mNativeWrapper = nativeWrapper;
|
||||
mActivePointerDisplayId = Display.INVALID_DISPLAY;
|
||||
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
|
||||
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
|
||||
}
|
||||
|
||||
void close() {
|
||||
@@ -90,7 +111,9 @@ class InputController {
|
||||
int productId,
|
||||
@NonNull IBinder deviceToken,
|
||||
int displayId) {
|
||||
final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId);
|
||||
final String phys = createPhys(PHYS_TYPE_KEYBOARD);
|
||||
setUniqueIdAssociation(displayId, phys);
|
||||
final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys);
|
||||
if (fd < 0) {
|
||||
throw new RuntimeException(
|
||||
"A native error occurred when creating keyboard: " + -fd);
|
||||
@@ -99,7 +122,7 @@ class InputController {
|
||||
synchronized (mLock) {
|
||||
mInputDeviceDescriptors.put(deviceToken,
|
||||
new InputDeviceDescriptor(fd, binderDeathRecipient,
|
||||
InputDeviceDescriptor.TYPE_KEYBOARD, displayId));
|
||||
InputDeviceDescriptor.TYPE_KEYBOARD, displayId, phys));
|
||||
}
|
||||
try {
|
||||
deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
|
||||
@@ -114,7 +137,9 @@ class InputController {
|
||||
int productId,
|
||||
@NonNull IBinder deviceToken,
|
||||
int displayId) {
|
||||
final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId);
|
||||
final String phys = createPhys(PHYS_TYPE_MOUSE);
|
||||
setUniqueIdAssociation(displayId, phys);
|
||||
final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys);
|
||||
if (fd < 0) {
|
||||
throw new RuntimeException(
|
||||
"A native error occurred when creating mouse: " + -fd);
|
||||
@@ -123,11 +148,9 @@ class InputController {
|
||||
synchronized (mLock) {
|
||||
mInputDeviceDescriptors.put(deviceToken,
|
||||
new InputDeviceDescriptor(fd, binderDeathRecipient,
|
||||
InputDeviceDescriptor.TYPE_MOUSE, displayId));
|
||||
final InputManagerInternal inputManagerInternal =
|
||||
LocalServices.getService(InputManagerInternal.class);
|
||||
inputManagerInternal.setVirtualMousePointerDisplayId(displayId);
|
||||
inputManagerInternal.setPointerAcceleration(1);
|
||||
InputDeviceDescriptor.TYPE_MOUSE, displayId, phys));
|
||||
mInputManagerInternal.setVirtualMousePointerDisplayId(displayId);
|
||||
mInputManagerInternal.setPointerAcceleration(1);
|
||||
mActivePointerDisplayId = displayId;
|
||||
}
|
||||
try {
|
||||
@@ -144,7 +167,9 @@ class InputController {
|
||||
@NonNull IBinder deviceToken,
|
||||
int displayId,
|
||||
@NonNull Point screenSize) {
|
||||
final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId,
|
||||
final String phys = createPhys(PHYS_TYPE_TOUCHSCREEN);
|
||||
setUniqueIdAssociation(displayId, phys);
|
||||
final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, phys,
|
||||
screenSize.y, screenSize.x);
|
||||
if (fd < 0) {
|
||||
throw new RuntimeException(
|
||||
@@ -154,7 +179,7 @@ class InputController {
|
||||
synchronized (mLock) {
|
||||
mInputDeviceDescriptors.put(deviceToken,
|
||||
new InputDeviceDescriptor(fd, binderDeathRecipient,
|
||||
InputDeviceDescriptor.TYPE_TOUCHSCREEN, displayId));
|
||||
InputDeviceDescriptor.TYPE_TOUCHSCREEN, displayId, phys));
|
||||
}
|
||||
try {
|
||||
deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
|
||||
@@ -174,6 +199,7 @@ class InputController {
|
||||
}
|
||||
token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
|
||||
mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
|
||||
InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys());
|
||||
|
||||
// Reset values to the default if all virtual mice are unregistered, or set display
|
||||
// id if there's another mouse (choose the most recent).
|
||||
@@ -197,9 +223,7 @@ class InputController {
|
||||
}
|
||||
}
|
||||
if (mostRecentlyCreatedMouse != null) {
|
||||
final InputManagerInternal inputManagerInternal =
|
||||
LocalServices.getService(InputManagerInternal.class);
|
||||
inputManagerInternal.setVirtualMousePointerDisplayId(
|
||||
mInputManagerInternal.setVirtualMousePointerDisplayId(
|
||||
mostRecentlyCreatedMouse.getDisplayId());
|
||||
mActivePointerDisplayId = mostRecentlyCreatedMouse.getDisplayId();
|
||||
} else {
|
||||
@@ -209,14 +233,21 @@ class InputController {
|
||||
}
|
||||
|
||||
private void resetMouseValuesLocked() {
|
||||
final InputManagerInternal inputManagerInternal =
|
||||
LocalServices.getService(InputManagerInternal.class);
|
||||
inputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
|
||||
inputManagerInternal.setPointerAcceleration(
|
||||
mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
|
||||
mInputManagerInternal.setPointerAcceleration(
|
||||
IInputConstants.DEFAULT_POINTER_ACCELERATION);
|
||||
mActivePointerDisplayId = Display.INVALID_DISPLAY;
|
||||
}
|
||||
|
||||
private static String createPhys(@PhysType String type) {
|
||||
return String.format("virtual%s:%d", type, sNextPhysId.getAndIncrement());
|
||||
}
|
||||
|
||||
private void setUniqueIdAssociation(int displayId, String phys) {
|
||||
final String displayUniqueId = mDisplayManagerInternal.getDisplayInfo(displayId).uniqueId;
|
||||
InputManager.getInstance().addUniqueIdAssociation(phys, displayUniqueId);
|
||||
}
|
||||
|
||||
boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
|
||||
synchronized (mLock) {
|
||||
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
|
||||
@@ -321,17 +352,18 @@ class InputController {
|
||||
fout.println(" creationOrder: "
|
||||
+ inputDeviceDescriptor.getCreationOrderNumber());
|
||||
fout.println(" type: " + inputDeviceDescriptor.getType());
|
||||
fout.println(" phys: " + inputDeviceDescriptor.getPhys());
|
||||
}
|
||||
fout.println(" Active mouse display id: " + mActivePointerDisplayId);
|
||||
}
|
||||
}
|
||||
|
||||
private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId,
|
||||
int productId);
|
||||
private static native int nativeOpenUinputMouse(String deviceName, int vendorId,
|
||||
int productId);
|
||||
int productId, String phys);
|
||||
private static native int nativeOpenUinputMouse(String deviceName, int vendorId, int productId,
|
||||
String phys);
|
||||
private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId,
|
||||
int productId, int height, int width);
|
||||
int productId, String phys, int height, int width);
|
||||
private static native boolean nativeCloseUinput(int fd);
|
||||
private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action);
|
||||
private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action);
|
||||
@@ -345,20 +377,18 @@ class InputController {
|
||||
/** Wrapper around the static native methods for tests. */
|
||||
@VisibleForTesting
|
||||
protected static class NativeWrapper {
|
||||
public int openUinputKeyboard(String deviceName, int vendorId, int productId) {
|
||||
return nativeOpenUinputKeyboard(deviceName, vendorId,
|
||||
productId);
|
||||
public int openUinputKeyboard(String deviceName, int vendorId, int productId, String phys) {
|
||||
return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys);
|
||||
}
|
||||
|
||||
public int openUinputMouse(String deviceName, int vendorId, int productId) {
|
||||
return nativeOpenUinputMouse(deviceName, vendorId,
|
||||
productId);
|
||||
public int openUinputMouse(String deviceName, int vendorId, int productId, String phys) {
|
||||
return nativeOpenUinputMouse(deviceName, vendorId, productId, phys);
|
||||
}
|
||||
|
||||
public int openUinputTouchscreen(String deviceName, int vendorId, int productId, int height,
|
||||
int width) {
|
||||
return nativeOpenUinputTouchscreen(deviceName, vendorId,
|
||||
productId, height, width);
|
||||
public int openUinputTouchscreen(String deviceName, int vendorId,
|
||||
int productId, String phys, int height, int width) {
|
||||
return nativeOpenUinputTouchscreen(deviceName, vendorId, productId, phys, height,
|
||||
width);
|
||||
}
|
||||
|
||||
public boolean closeUinput(int fd) {
|
||||
@@ -410,15 +440,17 @@ class InputController {
|
||||
private final IBinder.DeathRecipient mDeathRecipient;
|
||||
private final @Type int mType;
|
||||
private final int mDisplayId;
|
||||
private final String mPhys;
|
||||
// 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) {
|
||||
InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type,
|
||||
int displayId, String phys) {
|
||||
mFd = fd;
|
||||
mDeathRecipient = deathRecipient;
|
||||
mType = type;
|
||||
mDisplayId = displayId;
|
||||
mPhys = phys;
|
||||
mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement();
|
||||
}
|
||||
|
||||
@@ -445,6 +477,10 @@ class InputController {
|
||||
public long getCreationOrderNumber() {
|
||||
return mCreationOrderNumber;
|
||||
}
|
||||
|
||||
public String getPhys() {
|
||||
return mPhys;
|
||||
}
|
||||
}
|
||||
|
||||
private final class BinderDeathRecipient implements IBinder.DeathRecipient {
|
||||
|
||||
@@ -2285,14 +2285,8 @@ public class InputManagerService extends IInputManager.Stub
|
||||
nativeNotifyPortAssociationsChanged(mPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a runtime association between the input device name and the display unique id.
|
||||
* @param inputDeviceName The name of the input device.
|
||||
* @param displayUniqueId The unique id of the associated display.
|
||||
*/
|
||||
@Override // Binder call
|
||||
public void addUniqueIdAssociation(@NonNull String inputDeviceName,
|
||||
@NonNull String displayUniqueId) {
|
||||
public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) {
|
||||
if (!checkCallingPermission(
|
||||
android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
|
||||
"addNameAssociation()")) {
|
||||
@@ -2300,20 +2294,16 @@ public class InputManagerService extends IInputManager.Stub
|
||||
"Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
|
||||
}
|
||||
|
||||
Objects.requireNonNull(inputDeviceName);
|
||||
Objects.requireNonNull(inputPort);
|
||||
Objects.requireNonNull(displayUniqueId);
|
||||
synchronized (mAssociationsLock) {
|
||||
mUniqueIdAssociations.put(inputDeviceName, displayUniqueId);
|
||||
mUniqueIdAssociations.put(inputPort, displayUniqueId);
|
||||
}
|
||||
nativeChangeUniqueIdAssociation(mPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the runtime association between the input device and the display.
|
||||
* @param inputDeviceName The port of the input device to be cleared.
|
||||
*/
|
||||
@Override // Binder call
|
||||
public void removeUniqueIdAssociation(@NonNull String inputDeviceName) {
|
||||
public void removeUniqueIdAssociation(@NonNull String inputPort) {
|
||||
if (!checkCallingPermission(
|
||||
android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY,
|
||||
"removeUniqueIdAssociation()")) {
|
||||
@@ -2321,9 +2311,9 @@ public class InputManagerService extends IInputManager.Stub
|
||||
"Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission");
|
||||
}
|
||||
|
||||
Objects.requireNonNull(inputDeviceName);
|
||||
Objects.requireNonNull(inputPort);
|
||||
synchronized (mAssociationsLock) {
|
||||
mUniqueIdAssociations.remove(inputDeviceName);
|
||||
mUniqueIdAssociations.remove(inputPort);
|
||||
}
|
||||
nativeChangeUniqueIdAssociation(mPtr);
|
||||
}
|
||||
@@ -2594,6 +2584,13 @@ public class InputManagerService extends IInputManager.Stub
|
||||
pw.println(" display: " + v);
|
||||
});
|
||||
}
|
||||
if (!mUniqueIdAssociations.isEmpty()) {
|
||||
pw.println("Unique Id Associations:");
|
||||
mUniqueIdAssociations.forEach((k, v) -> {
|
||||
pw.print(" port: " + k);
|
||||
pw.println(" uniqueId: " + v);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ static std::map<int, int> KEY_CODE_MAPPING = {
|
||||
};
|
||||
|
||||
/** Creates a new uinput device and assigns a file descriptor. */
|
||||
static int openUinput(const char* readableName, jint vendorId, jint productId,
|
||||
static int openUinput(const char* readableName, jint vendorId, jint productId, const char* phys,
|
||||
DeviceType deviceType, jint screenHeight, jint screenWidth) {
|
||||
android::base::unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK)));
|
||||
if (fd < 0) {
|
||||
@@ -194,6 +194,8 @@ static int openUinput(const char* readableName, jint vendorId, jint productId,
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ioctl(fd, UI_SET_PHYS, phys);
|
||||
|
||||
ioctl(fd, UI_SET_EVBIT, EV_KEY);
|
||||
ioctl(fd, UI_SET_EVBIT, EV_SYN);
|
||||
switch (deviceType) {
|
||||
@@ -295,28 +297,30 @@ static int openUinput(const char* readableName, jint vendorId, jint productId,
|
||||
return fd.release();
|
||||
}
|
||||
|
||||
static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId,
|
||||
static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId, jstring phys,
|
||||
DeviceType deviceType, int screenHeight, int screenWidth) {
|
||||
ScopedUtfChars readableName(env, name);
|
||||
return openUinput(readableName.c_str(), vendorId, productId, deviceType, screenHeight,
|
||||
screenWidth);
|
||||
ScopedUtfChars readablePhys(env, phys);
|
||||
return openUinput(readableName.c_str(), vendorId, productId, readablePhys.c_str(), deviceType,
|
||||
screenHeight, screenWidth);
|
||||
}
|
||||
|
||||
static int nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
|
||||
jint productId) {
|
||||
return openUinputJni(env, name, vendorId, productId, DeviceType::KEYBOARD, /* screenHeight */ 0,
|
||||
/* screenWidth */ 0);
|
||||
jint productId, jstring phys) {
|
||||
return openUinputJni(env, name, vendorId, productId, phys, DeviceType::KEYBOARD,
|
||||
/* screenHeight */ 0, /* screenWidth */ 0);
|
||||
}
|
||||
|
||||
static int nativeOpenUinputMouse(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
|
||||
jint productId) {
|
||||
return openUinputJni(env, name, vendorId, productId, DeviceType::MOUSE, /* screenHeight */ 0,
|
||||
/* screenWidth */ 0);
|
||||
jint productId, jstring phys) {
|
||||
return openUinputJni(env, name, vendorId, productId, phys, DeviceType::MOUSE,
|
||||
/* screenHeight */ 0, /* screenWidth */ 0);
|
||||
}
|
||||
|
||||
static int nativeOpenUinputTouchscreen(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
|
||||
jint productId, jint height, jint width) {
|
||||
return openUinputJni(env, name, vendorId, productId, DeviceType::TOUCHSCREEN, height, width);
|
||||
jint productId, jstring phys, jint height, jint width) {
|
||||
return openUinputJni(env, name, vendorId, productId, phys, DeviceType::TOUCHSCREEN, height,
|
||||
width);
|
||||
}
|
||||
|
||||
static bool nativeCloseUinput(JNIEnv* env, jobject thiz, jint fd) {
|
||||
@@ -435,9 +439,11 @@ static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jint fd, jfloat xA
|
||||
}
|
||||
|
||||
static JNINativeMethod methods[] = {
|
||||
{"nativeOpenUinputKeyboard", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputKeyboard},
|
||||
{"nativeOpenUinputMouse", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputMouse},
|
||||
{"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IIII)I",
|
||||
{"nativeOpenUinputKeyboard", "(Ljava/lang/String;IILjava/lang/String;)I",
|
||||
(void*)nativeOpenUinputKeyboard},
|
||||
{"nativeOpenUinputMouse", "(Ljava/lang/String;IILjava/lang/String;)I",
|
||||
(void*)nativeOpenUinputMouse},
|
||||
{"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)I",
|
||||
(void*)nativeOpenUinputTouchscreen},
|
||||
{"nativeCloseUinput", "(I)Z", (void*)nativeCloseUinput},
|
||||
{"nativeWriteKeyEvent", "(III)Z", (void*)nativeWriteKeyEvent},
|
||||
|
||||
@@ -17,17 +17,23 @@
|
||||
package com.android.server.companion.virtual;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.hardware.display.DisplayManagerInternal;
|
||||
import android.hardware.input.IInputManager;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.hardware.input.InputManagerInternal;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInputConstants;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.view.Display;
|
||||
import android.view.DisplayInfo;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
@@ -46,18 +52,31 @@ public class InputControllerTest {
|
||||
@Mock
|
||||
private InputManagerInternal mInputManagerInternalMock;
|
||||
@Mock
|
||||
private DisplayManagerInternal mDisplayManagerInternalMock;
|
||||
@Mock
|
||||
private InputController.NativeWrapper mNativeWrapperMock;
|
||||
@Mock
|
||||
private IInputManager mIInputManagerMock;
|
||||
|
||||
private InputController mInputController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
|
||||
LocalServices.removeServiceForTest(InputManagerInternal.class);
|
||||
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
|
||||
|
||||
final DisplayInfo displayInfo = new DisplayInfo();
|
||||
displayInfo.uniqueId = "uniqueId";
|
||||
doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
|
||||
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
|
||||
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
|
||||
|
||||
InputManager.resetInstance(mIInputManagerMock);
|
||||
doNothing().when(mIInputManagerMock).addUniqueIdAssociation(anyString(), anyString());
|
||||
doNothing().when(mIInputManagerMock).removeUniqueIdAssociation(anyString());
|
||||
mInputController = new InputController(new Object(), mNativeWrapperMock);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.doCallRealMethod;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -57,6 +58,7 @@ import android.os.WorkSource;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
@@ -80,6 +82,8 @@ public class VirtualDeviceManagerServiceTest {
|
||||
private static final int DISPLAY_ID = 2;
|
||||
private static final int PRODUCT_ID = 10;
|
||||
private static final int VENDOR_ID = 5;
|
||||
private static final String UNIQUE_ID = "uniqueid";
|
||||
private static final String PHYS = "phys";
|
||||
private static final int HEIGHT = 1800;
|
||||
private static final int WIDTH = 900;
|
||||
private static final Binder BINDER = new Binder("binder");
|
||||
@@ -116,6 +120,12 @@ public class VirtualDeviceManagerServiceTest {
|
||||
LocalServices.removeServiceForTest(InputManagerInternal.class);
|
||||
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
|
||||
|
||||
final DisplayInfo displayInfo = new DisplayInfo();
|
||||
displayInfo.uniqueId = UNIQUE_ID;
|
||||
doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt());
|
||||
LocalServices.removeServiceForTest(DisplayManagerInternal.class);
|
||||
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
|
||||
|
||||
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
|
||||
doNothing().when(mContext).enforceCallingOrSelfPermission(
|
||||
eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
|
||||
@@ -274,7 +284,8 @@ public class VirtualDeviceManagerServiceTest {
|
||||
BINDER);
|
||||
assertWithMessage("Virtual keyboard should register fd when the display matches")
|
||||
.that(mInputController.mInputDeviceDescriptors).isNotEmpty();
|
||||
verify(mNativeWrapperMock).openUinputKeyboard(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
|
||||
verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME), eq(VENDOR_ID),
|
||||
eq(PRODUCT_ID), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -284,7 +295,8 @@ public class VirtualDeviceManagerServiceTest {
|
||||
BINDER);
|
||||
assertWithMessage("Virtual keyboard should register fd when the display matches")
|
||||
.that(mInputController.mInputDeviceDescriptors).isNotEmpty();
|
||||
verify(mNativeWrapperMock).openUinputMouse(DEVICE_NAME, VENDOR_ID, PRODUCT_ID);
|
||||
verify(mNativeWrapperMock).openUinputMouse(eq(DEVICE_NAME), eq(VENDOR_ID), eq(PRODUCT_ID),
|
||||
anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -294,8 +306,8 @@ public class VirtualDeviceManagerServiceTest {
|
||||
BINDER, new Point(WIDTH, HEIGHT));
|
||||
assertWithMessage("Virtual keyboard should register fd when the display matches")
|
||||
.that(mInputController.mInputDeviceDescriptors).isNotEmpty();
|
||||
verify(mNativeWrapperMock).openUinputTouchscreen(DEVICE_NAME, VENDOR_ID, PRODUCT_ID, HEIGHT,
|
||||
WIDTH);
|
||||
verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME), eq(VENDOR_ID),
|
||||
eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -315,7 +327,7 @@ public class VirtualDeviceManagerServiceTest {
|
||||
final int action = VirtualKeyEvent.ACTION_UP;
|
||||
mInputController.mInputDeviceDescriptors.put(BINDER,
|
||||
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 1,
|
||||
/* displayId= */ 1));
|
||||
/* displayId= */ 1, PHYS));
|
||||
mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder().setKeyCode(keyCode)
|
||||
.setAction(action).build());
|
||||
verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action);
|
||||
@@ -340,7 +352,7 @@ public class VirtualDeviceManagerServiceTest {
|
||||
final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
|
||||
mInputController.mInputDeviceDescriptors.put(BINDER,
|
||||
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
|
||||
/* displayId= */ 1));
|
||||
/* displayId= */ 1, PHYS));
|
||||
mInputController.mActivePointerDisplayId = 1;
|
||||
mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
|
||||
.setButtonCode(buttonCode)
|
||||
@@ -355,7 +367,7 @@ public class VirtualDeviceManagerServiceTest {
|
||||
final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
|
||||
mInputController.mInputDeviceDescriptors.put(BINDER,
|
||||
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
|
||||
/* displayId= */ 1));
|
||||
/* displayId= */ 1, PHYS));
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
@@ -381,7 +393,7 @@ public class VirtualDeviceManagerServiceTest {
|
||||
final float y = 0.7f;
|
||||
mInputController.mInputDeviceDescriptors.put(BINDER,
|
||||
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
|
||||
/* displayId= */ 1));
|
||||
/* displayId= */ 1, PHYS));
|
||||
mInputController.mActivePointerDisplayId = 1;
|
||||
mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
|
||||
.setRelativeX(x).setRelativeY(y).build());
|
||||
@@ -395,7 +407,7 @@ public class VirtualDeviceManagerServiceTest {
|
||||
final float y = 0.7f;
|
||||
mInputController.mInputDeviceDescriptors.put(BINDER,
|
||||
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
|
||||
/* displayId= */ 1));
|
||||
/* displayId= */ 1, PHYS));
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
@@ -422,7 +434,7 @@ public class VirtualDeviceManagerServiceTest {
|
||||
final float y = 1f;
|
||||
mInputController.mInputDeviceDescriptors.put(BINDER,
|
||||
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
|
||||
/* displayId= */ 1));
|
||||
/* displayId= */ 1, PHYS));
|
||||
mInputController.mActivePointerDisplayId = 1;
|
||||
mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
|
||||
.setXAxisMovement(x)
|
||||
@@ -437,7 +449,7 @@ public class VirtualDeviceManagerServiceTest {
|
||||
final float y = 1f;
|
||||
mInputController.mInputDeviceDescriptors.put(BINDER,
|
||||
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
|
||||
/* displayId= */ 1));
|
||||
/* displayId= */ 1, PHYS));
|
||||
assertThrows(
|
||||
IllegalStateException.class,
|
||||
() ->
|
||||
@@ -470,7 +482,7 @@ public class VirtualDeviceManagerServiceTest {
|
||||
final int action = VirtualTouchEvent.ACTION_UP;
|
||||
mInputController.mInputDeviceDescriptors.put(BINDER,
|
||||
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3,
|
||||
/* displayId= */ 1));
|
||||
/* displayId= */ 1, PHYS));
|
||||
mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
|
||||
.setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType).build());
|
||||
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN,
|
||||
@@ -489,7 +501,7 @@ public class VirtualDeviceManagerServiceTest {
|
||||
final float majorAxisSize = 10.0f;
|
||||
mInputController.mInputDeviceDescriptors.put(BINDER,
|
||||
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3,
|
||||
/* displayId= */ 1));
|
||||
/* displayId= */ 1, PHYS));
|
||||
mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x)
|
||||
.setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType)
|
||||
.setPressure(pressure).setMajorAxisSize(majorAxisSize).build());
|
||||
|
||||
Reference in New Issue
Block a user