diff options
author | Cody Schuffelen <schuffelen@google.com> | 2019-11-04 15:09:03 -0800 |
---|---|---|
committer | Cody Schuffelen <schuffelen@google.com> | 2019-11-07 15:05:47 -0800 |
commit | 038d4d98af2f40ed43df913b139bcba54cf8ce07 (patch) | |
tree | 5fd92e036e2745b33c46ee1d831ae5c6efe1ab9d | |
parent | 5b809d46f0ed136a5cc9e6900dfdbd5ef4a0694b (diff) | |
download | cuttlefish_common-038d4d98af2f40ed43df913b139bcba54cf8ce07.tar.gz |
Retrofit vsoc_input_service to use vsock
This allows using vsoc_input_service under qemu without the vsoc/ivshmem
support that is missing in 4.19 gki.
The alternative is to use virtio-input, but in qemu this requires
forwarding a hardware device (by using root permissions) while in crosvm
the same functionality is available through a file descriptor.
Test: launch_cvd -vm_manager=qemu_cli, check adb and vnc
Bug: 143713267
Change-Id: I5b7bdc063ff57aa3a455f77b8a7c23db29e1646e
-rw-r--r-- | guest/commands/vsoc_input_service/Android.bp | 7 | ||||
-rw-r--r-- | guest/commands/vsoc_input_service/main.cpp | 4 | ||||
-rw-r--r-- | guest/commands/vsoc_input_service/virtual_device_base.cpp | 3 | ||||
-rw-r--r-- | guest/commands/vsoc_input_service/vsoc_input_service.cpp | 95 | ||||
-rw-r--r-- | host/commands/assemble_cvd/flags.cc | 16 | ||||
-rw-r--r-- | host/commands/run_cvd/launch.cc | 73 | ||||
-rw-r--r-- | host/frontend/vnc_server/virtual_inputs.cpp | 52 | ||||
-rw-r--r-- | host/libs/config/cuttlefish_config.cpp | 20 | ||||
-rw-r--r-- | host/libs/config/cuttlefish_config.h | 6 |
9 files changed, 196 insertions, 80 deletions
diff --git a/guest/commands/vsoc_input_service/Android.bp b/guest/commands/vsoc_input_service/Android.bp index 76aae19f..73ca0671 100644 --- a/guest/commands/vsoc_input_service/Android.bp +++ b/guest/commands/vsoc_input_service/Android.bp @@ -24,12 +24,19 @@ cc_binary { "virtual_touchscreen.cpp", "vsoc_input_service.cpp", ], + static_libs: [ + "libgflags", + ], shared_libs: [ "cuttlefish_auto_resources", + "libcuttlefish_device_config", "libcuttlefish_fs", "libbase", "liblog", "vsoc_lib", ], + header_libs: [ + "cuttlefish_glog", + ], defaults: ["cuttlefish_guest_only"] } diff --git a/guest/commands/vsoc_input_service/main.cpp b/guest/commands/vsoc_input_service/main.cpp index 47b02a5f..c9e93ede 100644 --- a/guest/commands/vsoc_input_service/main.cpp +++ b/guest/commands/vsoc_input_service/main.cpp @@ -15,10 +15,12 @@ */ #include "log/log.h" +#include <gflags/gflags.h> #include "vsoc_input_service.h" -int main(int /* arg */, char* /* argv */[]) { +int main(int argc, char* argv[]) { + gflags::ParseCommandLineFlags(&argc, &argv, true); vsoc_input_service::VSoCInputService service; if (!service.SetUpDevices()) { return -1; diff --git a/guest/commands/vsoc_input_service/virtual_device_base.cpp b/guest/commands/vsoc_input_service/virtual_device_base.cpp index d0c16bda..0abb0cd9 100644 --- a/guest/commands/vsoc_input_service/virtual_device_base.cpp +++ b/guest/commands/vsoc_input_service/virtual_device_base.cpp @@ -16,6 +16,7 @@ #include "virtual_device_base.h" +#include <glog/logging.h> #include <errno.h> #include <inttypes.h> #include <string.h> @@ -103,6 +104,8 @@ bool VirtualDeviceBase::SetUp() { return false; } + LOG(INFO) << "set up virtual device"; + return true; } diff --git a/guest/commands/vsoc_input_service/vsoc_input_service.cpp b/guest/commands/vsoc_input_service/vsoc_input_service.cpp index d100a527..8753735c 100644 --- a/guest/commands/vsoc_input_service/vsoc_input_service.cpp +++ b/guest/commands/vsoc_input_service/vsoc_input_service.cpp @@ -18,37 +18,35 @@ #include <linux/input.h> #include <linux/uinput.h> +#include <linux/virtio_input.h> #include <thread> +#include <gflags/gflags.h> #include "log/log.h" +#include <glog/logging.h> -#include "common/vsoc/lib/screen_region_view.h" +#include "common/libs/fs/shared_fd.h" +#include "common/libs/device_config/device_config.h" #include "common/vsoc/lib/input_events_region_view.h" -using vsoc::screen::ScreenRegionView; using vsoc::input_events::InputEvent; -using vsoc::input_events::InputEventsRegionView; using vsoc_input_service::VirtualDeviceBase; using vsoc_input_service::VirtualKeyboard; using vsoc_input_service::VirtualPowerButton; using vsoc_input_service::VirtualTouchScreen; using vsoc_input_service::VSoCInputService; +DEFINE_uint32(keyboard_port, 0, "keyboard vsock port"); +DEFINE_uint32(touch_port, 0, "keyboard vsock port"); + namespace { void EventLoop(std::shared_ptr<VirtualDeviceBase> device, - std::function<int(InputEvent*, int)> next_events) { + std::function<InputEvent()> next_event) { while (1) { - InputEvent events[InputEventsRegionView::kMaxEventsPerPacket]; - int count = next_events(events, InputEventsRegionView::kMaxEventsPerPacket); - if (count <= 0) { - SLOGE("Error getting events from the queue: Maybe check packet size"); - continue; - } - for (int i = 0; i < count; ++i) { - device->EmitEvent(events[i].type, events[i].code, events[i].value); - } + InputEvent event = next_event(); + device->EmitEvent(event.type, event.code, event.value); } } @@ -64,14 +62,14 @@ bool VSoCInputService::SetUpDevices() { return false; } - auto screen_view = ScreenRegionView::GetInstance(); - if (!screen_view) { - SLOGE("Failed to open framebuffer broadcast region"); + auto config = cvd::DeviceConfig::Get(); + if (!config) { + LOG(ERROR) << "Failed to open device config"; return false; } virtual_touchscreen_.reset( - new VirtualTouchScreen(screen_view->x_res(), screen_view->y_res())); + new VirtualTouchScreen(config->screen_x_res(), config->screen_y_res())); if (!virtual_touchscreen_->SetUp()) { return false; } @@ -80,35 +78,56 @@ bool VSoCInputService::SetUpDevices() { } bool VSoCInputService::ProcessEvents() { - auto input_events_rv = InputEventsRegionView::GetInstance(); - // TODO(jemoreira): Post available devices to region - auto worker = input_events_rv->StartWorker(); + cvd::SharedFD keyboard_fd; + cvd::SharedFD touch_fd; + + LOG(INFO) << "Connecting to the keyboard at " << FLAGS_keyboard_port; + if (FLAGS_keyboard_port) { + keyboard_fd = cvd::SharedFD::VsockClient(2, FLAGS_keyboard_port, SOCK_STREAM); + if (!keyboard_fd->IsOpen()) { + LOG(ERROR) << "Could not connect to the keyboard at vsock:2:" << FLAGS_keyboard_port; + } + LOG(INFO) << "Connected to keyboard"; + } + LOG(INFO) << "Connecting to the touchscreen at " << FLAGS_keyboard_port; + if (FLAGS_touch_port) { + touch_fd = cvd::SharedFD::VsockClient(2, FLAGS_touch_port, SOCK_STREAM); + if (!touch_fd->IsOpen()) { + LOG(ERROR) << "Could not connect to the touch at vsock:2:" << FLAGS_touch_port; + } + LOG(INFO) << "Connected to touch"; + } // Start device threads - std::thread screen_thread([this]() { - EventLoop( - virtual_touchscreen_, [](InputEvent* event_buffer, int max_events) { - return InputEventsRegionView::GetInstance()->GetScreenEventsOrWait( - event_buffer, max_events); - }); - }); - std::thread keyboard_thread([this]() { - EventLoop(virtual_keyboard_, [](InputEvent* event_buffer, int max_events) { - return InputEventsRegionView::GetInstance()->GetKeyboardEventsOrWait( - event_buffer, max_events); + std::thread screen_thread([this, touch_fd]() { + EventLoop(virtual_touchscreen_, [touch_fd]() { + struct virtio_input_event event; + if (touch_fd->Read(&event, sizeof(event)) != sizeof(event)) { + LOG(FATAL) << "Could not read touch event: " << touch_fd->StrError(); + } + return InputEvent { + .type = event.type, + .code = event.code, + .value = event.value, + }; }); }); - std::thread button_thread([this]() { - EventLoop(virtual_power_button_, - [](InputEvent* event_buffer, int max_events) { - return InputEventsRegionView::GetInstance() - ->GetPowerButtonEventsOrWait(event_buffer, max_events); - }); + std::thread keyboard_thread([this, keyboard_fd]() { + EventLoop(virtual_keyboard_, [keyboard_fd]() { + struct virtio_input_event event; + if (keyboard_fd->Read(&event, sizeof(event)) != sizeof(event)) { + LOG(FATAL) << "Could not read keyboard event: " << keyboard_fd->StrError(); + } + return InputEvent { + .type = event.type, + .code = event.code, + .value = event.value, + }; + }); }); screen_thread.join(); keyboard_thread.join(); - button_thread.join(); // Should never return return false; diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc index 60a5dcda..af7d336d 100644 --- a/host/commands/assemble_cvd/flags.cc +++ b/host/commands/assemble_cvd/flags.cc @@ -204,6 +204,10 @@ DEFINE_string(tombstone_receiver_binary, "Binary for the tombstone server"); DEFINE_int32(tombstone_receiver_port, vsoc::GetPerInstanceDefault(5630), "The vsock port for tombstones"); +DEFINE_int32(keyboard_server_port, GetPerInstanceDefault(5540), + "The port on which the vsock keyboard server should listen"); +DEFINE_int32(touch_server_port, GetPerInstanceDefault(5640), + "The port on which the vsock touch server should listen"); DEFINE_bool(use_bootloader, false, "Boots the device using a bootloader"); DEFINE_string(bootloader, "", "Bootloader binary path"); DEFINE_string(boot_slot, "", "Force booting into the given slot. If empty, " @@ -522,6 +526,15 @@ bool InitializeCuttlefishConfiguration( tmp_config_obj.add_kernel_cmdline("androidboot.tombstone_transmit=0"); } + tmp_config_obj.set_touch_socket_port(FLAGS_touch_server_port); + tmp_config_obj.set_keyboard_socket_port(FLAGS_keyboard_server_port); + if (FLAGS_vm_manager == vm_manager::QemuManager::name()) { + tmp_config_obj.add_kernel_cmdline(concat("androidboot.vsock_touch_port=", + FLAGS_touch_server_port)); + tmp_config_obj.add_kernel_cmdline(concat("androidboot.vsock_keyboard_port=", + FLAGS_keyboard_server_port)); + } + tmp_config_obj.set_use_bootloader(FLAGS_use_bootloader); tmp_config_obj.set_bootloader(FLAGS_bootloader); @@ -567,7 +580,8 @@ void SetDefaultFlagsForQemu() { // TODO(b/144111429): Consolidate to one hardware name SetCommandLineOptionWithMode("hardware_name", "cutf_cvm", google::FlagSettingMode::SET_FLAGS_DEFAULT); - SetCommandLineOptionWithMode("logcat_mode", cvd::kLogcatSerialMode, + // TODO(b/144119457) Use the serial port. + SetCommandLineOptionWithMode("logcat_mode", cvd::kLogcatVsockMode, google::FlagSettingMode::SET_FLAGS_DEFAULT); } diff --git a/host/commands/run_cvd/launch.cc b/host/commands/run_cvd/launch.cc index 47cf2e26..5c42e593 100644 --- a/host/commands/run_cvd/launch.cc +++ b/host/commands/run_cvd/launch.cc @@ -12,6 +12,8 @@ #include "host/commands/run_cvd/runner_defs.h" #include "host/commands/run_cvd/pre_launch_initializers.h" #include "host/commands/run_cvd/vsoc_shared_memory.h" +#include "host/libs/vm_manager/crosvm_manager.h" +#include "host/libs/vm_manager/qemu_manager.h" using cvd::RunnerExitCodes; using cvd::MonitorEntry; @@ -246,10 +248,20 @@ void LaunchUsbServerIfEnabled(const vsoc::CuttlefishConfig& config, GetOnSubprocessExitCallback(config)); } -cvd::SharedFD CreateVncInputServer(const std::string& path) { +cvd::SharedFD CreateUnixVncInputServer(const std::string& path) { auto server = cvd::SharedFD::SocketLocalServer(path.c_str(), false, SOCK_STREAM, 0666); if (!server->IsOpen()) { - LOG(ERROR) << "Unable to create mouse server: " + LOG(ERROR) << "Unable to create unix input server: " + << server->StrError(); + return cvd::SharedFD(); + } + return server; +} + +cvd::SharedFD CreateVsockVncInputServer(int port) { + auto server = cvd::SharedFD::VsockServer(port, SOCK_STREAM); + if (!server->IsOpen()) { + LOG(ERROR) << "Unable to create vsock input server: " << server->StrError(); return cvd::SharedFD(); } @@ -264,33 +276,38 @@ bool LaunchVNCServerIfEnabled(const vsoc::CuttlefishConfig& config, auto port_options = "-port=" + std::to_string(config.vnc_server_port()); cvd::Command vnc_server(config.vnc_server_binary()); vnc_server.AddParameter(port_options); - if (!config.enable_ivserver()) { - // When the ivserver is not enabled, the vnc touch_server needs to serve - // on unix sockets and send input events to whoever connects to it (namely - // crosvm) - auto touch_server = CreateVncInputServer(config.touch_socket_path()); - if (!touch_server->IsOpen()) { - return false; - } - vnc_server.AddParameter("-touch_fd=", touch_server); - - auto keyboard_server = - CreateVncInputServer(config.keyboard_socket_path()); - if (!keyboard_server->IsOpen()) { - return false; - } - vnc_server.AddParameter("-keyboard_fd=", keyboard_server); - // TODO(b/128852363): This should be handled through the wayland mock - // instead. - // Additionally it receives the frame updates from a virtual socket - // instead - auto frames_server = - cvd::SharedFD::VsockServer(config.frames_vsock_port(), SOCK_STREAM); - if (!frames_server->IsOpen()) { - return false; - } - vnc_server.AddParameter("-frame_server_fd=", frames_server); + if (config.vm_manager() == vm_manager::QemuManager::name()) { + vnc_server.AddParameter("-write_virtio_input"); + } + // When the ivserver is not enabled, the vnc touch_server needs to serve + // on sockets and send input events to whoever connects to it (the VMM). + auto touch_server = + config.vm_manager() == vm_manager::CrosvmManager::name() + ? CreateUnixVncInputServer(config.touch_socket_path()) + : CreateVsockVncInputServer(config.touch_socket_port()); + if (!touch_server->IsOpen()) { + return false; + } + vnc_server.AddParameter("-touch_fd=", touch_server); + + auto keyboard_server = + config.vm_manager() == vm_manager::CrosvmManager::name() + ? CreateUnixVncInputServer(config.keyboard_socket_path()) + : CreateVsockVncInputServer(config.keyboard_socket_port()); + if (!keyboard_server->IsOpen()) { + return false; + } + vnc_server.AddParameter("-keyboard_fd=", keyboard_server); + // TODO(b/128852363): This should be handled through the wayland mock + // instead. + // Additionally it receives the frame updates from a virtual socket + // instead + auto frames_server = + cvd::SharedFD::VsockServer(config.frames_vsock_port(), SOCK_STREAM); + if (!frames_server->IsOpen()) { + return false; } + vnc_server.AddParameter("-frame_server_fd=", frames_server); process_monitor->StartSubprocess(std::move(vnc_server), callback); return true; } diff --git a/host/frontend/vnc_server/virtual_inputs.cpp b/host/frontend/vnc_server/virtual_inputs.cpp index e7e37577..31c03282 100644 --- a/host/frontend/vnc_server/virtual_inputs.cpp +++ b/host/frontend/vnc_server/virtual_inputs.cpp @@ -19,6 +19,7 @@ #include <linux/input.h> #include <linux/uinput.h> +#include <cstdint> #include <mutex> #include "keysyms.h" @@ -34,7 +35,18 @@ DEFINE_int32(touch_fd, -1, DEFINE_int32(keyboard_fd, -1, "A fd for a socket where to accept keyboard connections"); +DEFINE_bool(write_virtio_input, false, + "Whether to write the virtio_input struct over the socket"); + namespace { +// Necessary to define here as the virtio_input.h header is not available +// in the host glibc. +struct virtio_input_event { + std::uint16_t type; + std::uint16_t code; + std::int32_t value; +}; + void AddKeyMappings(std::map<uint32_t, uint16_t>* key_mapping) { (*key_mapping)[cvd::xk::AltLeft] = KEY_LEFTALT; (*key_mapping)[cvd::xk::ControlLeft] = KEY_LEFTCTRL; @@ -274,7 +286,7 @@ class SocketVirtualInputs : public VirtualInputs { InitInputEvent(&events[0], EV_KEY, keymapping_[key_code], down); InitInputEvent(&events[1], EV_SYN, 0, 0); - SendEvents(keyboard_socket_, events, sizeof(events)); + SendEvents(keyboard_socket_, events); } void PressPowerButton(bool down) override { @@ -282,7 +294,7 @@ class SocketVirtualInputs : public VirtualInputs { InitInputEvent(&events[0], EV_KEY, KEY_POWER, down); InitInputEvent(&events[1], EV_SYN, 0, 0); - SendEvents(keyboard_socket_, events, sizeof(events)); + SendEvents(keyboard_socket_, events); } void HandlePointerEvent(bool touch_down, int x, int y) override { @@ -293,11 +305,12 @@ class SocketVirtualInputs : public VirtualInputs { InitInputEvent(&events[2], EV_KEY, BTN_TOUCH, touch_down); InitInputEvent(&events[3], EV_SYN, 0, 0); - SendEvents(touch_socket_, events, sizeof(events)); + SendEvents(touch_socket_, events); } private: - void SendEvents(cvd::SharedFD socket, void* event_buffer, int byte_count) { + template<size_t num_events> + void SendEvents(cvd::SharedFD socket, struct input_event (&event_buffer)[num_events]) { std::lock_guard<std::mutex> lock(socket_mutex_); if (!socket->IsOpen()) { // This is unlikely as it would only happen between the start of the vnc @@ -306,9 +319,25 @@ class SocketVirtualInputs : public VirtualInputs { // handle it. return; } - auto ret = socket->Write(event_buffer, byte_count); - if (ret < 0) { - LOG(ERROR) << "Error sending input event: " << socket->StrError(); + + if (FLAGS_write_virtio_input) { + struct virtio_input_event virtio_events[num_events]; + for (size_t i = 0; i < num_events; i++) { + virtio_events[i] = (struct virtio_input_event) { + .type = event_buffer[i].type, + .code = event_buffer[i].code, + .value = event_buffer[i].value, + }; + } + auto ret = socket->Write(virtio_events, sizeof(virtio_events)); + if (ret < 0) { + LOG(ERROR) << "Error sending input events: " << socket->StrError(); + } + } else { + auto ret = socket->Write(event_buffer, sizeof(event_buffer)); + if (ret < 0) { + LOG(ERROR) << "Error sending input events: " << socket->StrError(); + } } } @@ -320,6 +349,7 @@ class SocketVirtualInputs : public VirtualInputs { auto keyboard_server = cvd::SharedFD::Dup(FLAGS_keyboard_fd); close(FLAGS_keyboard_fd); FLAGS_keyboard_fd = -1; + LOG(INFO) << "Input socket host accepting connections..."; while (1) { cvd::SharedFDSet read_set; @@ -330,9 +360,11 @@ class SocketVirtualInputs : public VirtualInputs { std::lock_guard<std::mutex> lock(socket_mutex_); if (read_set.IsSet(touch_server)) { touch_socket_ = cvd::SharedFD::Accept(*touch_server); + LOG(INFO) << "connected to touch"; } if (read_set.IsSet(keyboard_server)) { keyboard_socket_ = cvd::SharedFD::Accept(*keyboard_server); + LOG(INFO) << "connected to keyboard"; } } } @@ -346,9 +378,5 @@ class SocketVirtualInputs : public VirtualInputs { VirtualInputs::VirtualInputs() { AddKeyMappings(&keymapping_); } VirtualInputs* VirtualInputs::Get() { - if (vsoc::CuttlefishConfig::Get()->enable_ivserver()) { - return new VSoCVirtualInputs(); - } else { - return new SocketVirtualInputs(); - } + return new SocketVirtualInputs(); } diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp index 882a951e..17dcb6e5 100644 --- a/host/libs/config/cuttlefish_config.cpp +++ b/host/libs/config/cuttlefish_config.cpp @@ -168,6 +168,10 @@ const char* kBootloader = "bootloader"; const char* kUseBootloader = "use_bootloader"; const char* kBootSlot = "boot_slot"; + +const char* kTouchSocketPort = "touch_socket_port"; +const char* kKeyboardSocketPort = "keyboard_socket_port"; + } // namespace namespace vsoc { @@ -938,6 +942,22 @@ std::string CuttlefishConfig::keyboard_socket_path() const { return PerInstanceInternalPath("keyboard.sock"); } +void CuttlefishConfig::set_touch_socket_port(int port) { + (*dictionary_)[kTouchSocketPort] = port; +} + +int CuttlefishConfig::touch_socket_port() const { + return (*dictionary_)[kTouchSocketPort].asInt(); +} + +void CuttlefishConfig::set_keyboard_socket_port(int port) { + (*dictionary_)[kKeyboardSocketPort] = port; +} + +int CuttlefishConfig::keyboard_socket_port() const { + return (*dictionary_)[kKeyboardSocketPort].asInt(); +} + // Creates the (initially empty) config object and populates it with values from // the config file if the CUTTLEFISH_CONFIG_FILE env variable is present. // Returns nullptr if there was an error loading from file diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h index 0fca9f0d..fc479ec7 100644 --- a/host/libs/config/cuttlefish_config.h +++ b/host/libs/config/cuttlefish_config.h @@ -356,6 +356,12 @@ class CuttlefishConfig { std::string touch_socket_path() const; std::string keyboard_socket_path() const; + void set_touch_socket_port(int touch_socket_port); + int touch_socket_port() const; + + void set_keyboard_socket_port(int keyboard_socket_port); + int keyboard_socket_port() const; + private: std::unique_ptr<Json::Value> dictionary_; |