Support trackpad in VM
It handles single touch and tap event for now.
Bug: 347253952
Test: add trackpad, and then check if it works
Change-Id: Ifec4219036b4baa66c84faffa9d3b22f99fdddd4
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachine.java b/java/framework/src/android/system/virtualmachine/VirtualMachine.java
index f5ce102..d54bd44 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachine.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachine.java
@@ -173,6 +173,7 @@
private ParcelFileDescriptor mKeySock;
private ParcelFileDescriptor mMouseSock;
private ParcelFileDescriptor mSwitchesSock;
+ private ParcelFileDescriptor mTrackpadSock;
/**
* Status of a virtual machine
@@ -929,6 +930,16 @@
s.pfd = pfds[1];
inputDevices.add(InputDevice.switches(s));
}
+ if (vmConfig.getCustomImageConfig().useTrackpad()) {
+ ParcelFileDescriptor[] pfds = ParcelFileDescriptor.createSocketPair();
+ mTrackpadSock = pfds[0];
+ InputDevice.Trackpad t = new InputDevice.Trackpad();
+ // TODO(b/347253952): make it configurable
+ t.width = 2380;
+ t.height = 1369;
+ t.pfd = pfds[1];
+ inputDevices.add(InputDevice.trackpad(t));
+ }
}
rawConfig.inputDevices = inputDevices.toArray(new InputDevice[0]);
@@ -1096,6 +1107,65 @@
new InputEvent(EV_SYN, SYN_REPORT, 0)));
}
+ /** @hide */
+ public boolean sendTrackpadEvent(MotionEvent event) {
+ if (mTrackpadSock == null) {
+ Log.d(TAG, "mTrackpadSock == null");
+ return false;
+ }
+ // from include/uapi/linux/input-event-codes.h in the kernel.
+ short EV_SYN = 0x00;
+ short EV_ABS = 0x03;
+ short EV_KEY = 0x01;
+ short BTN_TOUCH = 0x14a;
+ short BTN_TOOL_FINGER = 0x145;
+ short ABS_X = 0x00;
+ short ABS_Y = 0x01;
+ short SYN_REPORT = 0x00;
+ short ABS_MT_SLOT = 0x2f;
+ short ABS_MT_TOUCH_MAJOR = 0x30;
+ short ABS_MT_TOUCH_MINOR = 0x31;
+ short ABS_MT_WIDTH_MAJOR = 0x32;
+ short ABS_MT_WIDTH_MINOR = 0x33;
+ short ABS_MT_ORIENTATION = 0x34;
+ short ABS_MT_POSITION_X = 0x35;
+ short ABS_MT_POSITION_Y = 0x36;
+ short ABS_MT_TOOL_TYPE = 0x37;
+ short ABS_MT_BLOB_ID = 0x38;
+ short ABS_MT_TRACKING_ID = 0x39;
+ short ABS_MT_PRESSURE = 0x3a;
+ short ABS_MT_DISTANCE = 0x3b;
+ short ABS_MT_TOOL_X = 0x3c;
+ short ABS_MT_TOOL_Y = 0x3d;
+ short ABS_PRESSURE = 0x18;
+ short ABS_TOOL_WIDTH = 0x1c;
+
+ int x = (int) event.getRawX();
+ int y = (int) event.getRawY();
+ boolean down = event.getAction() != MotionEvent.ACTION_UP;
+
+ // TODO(b/347253952): support multi-touch and button click
+ return writeEventsToSock(
+ mTrackpadSock,
+ Arrays.asList(
+ new InputEvent(EV_KEY, BTN_TOUCH, down ? 1 : 0),
+ new InputEvent(EV_KEY, BTN_TOOL_FINGER, down ? 1 : 0),
+ new InputEvent(EV_ABS, ABS_MT_SLOT, 0),
+ new InputEvent(
+ EV_ABS, ABS_MT_TRACKING_ID, down ? event.getPointerId(0) : -1),
+ new InputEvent(EV_ABS, ABS_MT_TOOL_TYPE, 0 /* MT_TOOL_FINGER */),
+ new InputEvent(EV_ABS, ABS_MT_POSITION_X, x),
+ new InputEvent(EV_ABS, ABS_MT_POSITION_Y, y),
+ new InputEvent(EV_ABS, ABS_MT_TOUCH_MAJOR, (short) event.getTouchMajor()),
+ new InputEvent(EV_ABS, ABS_MT_TOUCH_MINOR, (short) event.getTouchMinor()),
+ new InputEvent(EV_ABS, ABS_X, x),
+ new InputEvent(EV_ABS, ABS_Y, y),
+ new InputEvent(EV_ABS, ABS_PRESSURE, (short) (255 * event.getPressure())),
+ new InputEvent(
+ EV_ABS, ABS_MT_PRESSURE, (short) (255 * event.getPressure())),
+ new InputEvent(EV_SYN, SYN_REPORT, 0)));
+ }
+
private boolean writeEventsToSock(ParcelFileDescriptor sock, List<InputEvent> evtList) {
ByteBuffer byteBuffer =
ByteBuffer.allocate(8 /* (type: u16 + code: u16 + value: i32) */ * evtList.size());
diff --git a/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java b/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
index 2a571ff..3a1c784 100644
--- a/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
+++ b/java/framework/src/android/system/virtualmachine/VirtualMachineCustomImageConfig.java
@@ -44,6 +44,7 @@
private static final String KEY_NETWORK = "network";
private static final String KEY_GPU = "gpu";
private static final String KEY_AUDIO_CONFIG = "audio_config";
+ private static final String KEY_TRACKPAD = "trackpad";
@Nullable private final String name;
@Nullable private final String kernelPath;
@@ -59,6 +60,7 @@
private final boolean switches;
private final boolean network;
@Nullable private final GpuConfig gpuConfig;
+ private final boolean trackpad;
@Nullable
public Disk[] getDisks() {
@@ -106,6 +108,10 @@
return switches;
}
+ public boolean useTrackpad() {
+ return mouse;
+ }
+
public boolean useNetwork() {
return network;
}
@@ -125,7 +131,8 @@
boolean switches,
boolean network,
GpuConfig gpuConfig,
- AudioConfig audioConfig) {
+ AudioConfig audioConfig,
+ boolean trackpad) {
this.name = name;
this.kernelPath = kernelPath;
this.initrdPath = initrdPath;
@@ -140,6 +147,7 @@
this.network = network;
this.gpuConfig = gpuConfig;
this.audioConfig = audioConfig;
+ this.trackpad = trackpad;
}
static VirtualMachineCustomImageConfig from(PersistableBundle customImageConfigBundle) {
@@ -190,6 +198,7 @@
PersistableBundle audioConfigPb =
customImageConfigBundle.getPersistableBundle(KEY_AUDIO_CONFIG);
builder.setAudioConfig(AudioConfig.from(audioConfigPb));
+ builder.useTrackpad(customImageConfigBundle.getBoolean(KEY_TRACKPAD));
return builder.build();
}
@@ -248,6 +257,7 @@
pb.putPersistableBundle(
KEY_AUDIO_CONFIG,
Optional.ofNullable(audioConfig).map(ac -> ac.toPersistableBundle()).orElse(null));
+ pb.putBoolean(KEY_TRACKPAD, trackpad);
return pb;
}
@@ -341,6 +351,7 @@
private boolean switches;
private boolean network;
private GpuConfig gpuConfig;
+ private boolean trackpad;
/** @hide */
public Builder() {}
@@ -418,6 +429,12 @@
}
/** @hide */
+ public Builder useTrackpad(boolean trackpad) {
+ this.trackpad = trackpad;
+ return this;
+ }
+
+ /** @hide */
public Builder useNetwork(boolean network) {
this.network = network;
return this;
@@ -445,7 +462,8 @@
switches,
network,
gpuConfig,
- audioConfig);
+ audioConfig,
+ trackpad);
}
}
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index 671a012..425beed 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -808,6 +808,12 @@
InputDevice::Switches(switches) => InputDeviceOption::Switches(clone_file(
switches.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?,
)?),
+ InputDevice::Trackpad(trackpad) => InputDeviceOption::MultiTouchTrackpad {
+ file: clone_file(trackpad.pfd.as_ref().ok_or(anyhow!("pfd should have value"))?)?,
+ height: u32::try_from(trackpad.height)?,
+ width: u32::try_from(trackpad.width)?,
+ name: if !trackpad.name.is_empty() { Some(trackpad.name.clone()) } else { None },
+ },
})
}
/// Given the configuration for a disk image, assembles the `DiskFile` to pass to crosvm.
diff --git a/virtualizationmanager/src/crosvm.rs b/virtualizationmanager/src/crosvm.rs
index a9a91fe..1998ef7 100644
--- a/virtualizationmanager/src/crosvm.rs
+++ b/virtualizationmanager/src/crosvm.rs
@@ -222,6 +222,7 @@
Keyboard(File),
Mouse(File),
Switches(File),
+ MultiTouchTrackpad { file: File, width: u32, height: u32, name: Option<String> },
}
type VfioDevice = Strong<dyn IBoundDevice>;
@@ -1154,6 +1155,13 @@
InputDeviceOption::Switches(file) => {
format!("switches[path={}]", add_preserved_fd(&mut preserved_fds, file))
}
+ InputDeviceOption::MultiTouchTrackpad { file, width, height, name } => format!(
+ "multi-touch-trackpad[path={},width={},height={}{}]",
+ add_preserved_fd(&mut preserved_fds, file),
+ width,
+ height,
+ name.as_ref().map_or("".into(), |n| format!(",name={}", n))
+ ),
});
}
}
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/InputDevice.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/InputDevice.aidl
index 5a7ed4a..e998d02 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/InputDevice.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/InputDevice.aidl
@@ -44,9 +44,17 @@
ParcelFileDescriptor pfd;
}
+ parcelable Trackpad {
+ ParcelFileDescriptor pfd;
+ // Default values come from https://crosvm.dev/book/devices/input.html#trackpad
+ int width = 1280;
+ int height = 1080;
+ @utf8InCpp String name = "";
+ }
SingleTouch singleTouch;
EvDev evDev;
Keyboard keyboard;
Mouse mouse;
Switches switches;
+ Trackpad trackpad;
}
diff --git a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
index ef94be5..c3dc189 100644
--- a/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
+++ b/vmlauncher_app/java/com/android/virtualization/vmlauncher/MainActivity.java
@@ -219,6 +219,7 @@
customImageConfigBuilder.useKeyboard(true);
customImageConfigBuilder.useMouse(true);
customImageConfigBuilder.useSwitches(true);
+ customImageConfigBuilder.useTrackpad(true);
customImageConfigBuilder.useNetwork(true);
AudioConfig.Builder audioConfigBuilder = new AudioConfig.Builder();
@@ -359,6 +360,10 @@
if (mVirtualMachine == null) {
return false;
}
+ int eventSource = event.getSource();
+ if ((eventSource & InputDevice.SOURCE_CLASS_POSITION) != 0) {
+ return mVirtualMachine.sendTrackpadEvent(event);
+ }
return mVirtualMachine.sendMouseEvent(event);
});
surfaceView