From a7952ac14158f130e6381011ca7d308dc47c7660 Mon Sep 17 00:00:00 2001 From: Xihua Chen Date: Fri, 12 Jan 2018 13:42:18 +0800 Subject: minui: Support input device hotplug in recovery mode. In the old code, the recovery only enumerated the input devices at the startup, and read the input events from these devices. So if a USB input device is probed after the recovery startup, then the recovery can't read the events from this device. This patch use inotify to monitor /dev/input for new added input device, then support input device hotplug in recovery mode. Bug: 111847510 Test: can use USB keyboard hotplugged in recovery mode Change-Id: I7e7dcbd619d3c66a2f40a43418f5dac6a50c859e Signed-off-by: Liu Shuo A Signed-off-by: Ming Tan --- minui/events.cpp | 124 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 104 insertions(+), 20 deletions(-) diff --git a/minui/events.cpp b/minui/events.cpp index 7d0250e97..f331ed68a 100644 --- a/minui/events.cpp +++ b/minui/events.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,8 @@ #include "minui/minui.h" +constexpr const char* INPUT_DEV_DIR = "/dev/input"; + constexpr size_t MAX_DEVICES = 16; constexpr size_t MAX_MISC_FDS = 16; @@ -46,6 +49,8 @@ struct FdInfo { ev_callback cb; }; +static bool g_allow_touch_inputs = true; +static ev_callback g_saved_input_cb; static android::base::unique_fd g_epoll_fd; static epoll_event g_polled_events[MAX_DEVICES + MAX_MISC_FDS]; static int g_polled_events_count; @@ -60,6 +65,78 @@ static bool test_bit(size_t bit, unsigned long* array) { // NOLINT return (array[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0; } +static bool should_add_input_device(int fd, bool allow_touch_inputs) { + // Use unsigned long to match ioctl's parameter type. + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT + + // Read the evbits of the input device. + if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + return false; + } + + // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also + // allowed if allow_touch_inputs is set. + if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) { + if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) { + return false; + } + } + + return true; +} + +static int inotify_cb(int fd, __unused uint32_t epevents) { + if (g_saved_input_cb == nullptr) return -1; + + // The inotify will put one or several complete events. + // Should not read part of one event. + size_t event_len; + int ret = ioctl(fd, FIONREAD, &event_len); + if (ret != 0) return -1; + + std::unique_ptr dir(opendir(INPUT_DEV_DIR), closedir); + if (!dir) { + return -1; + } + + std::vector buf(event_len); + + ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf.data(), event_len)); + if (r != event_len) { + return -1; + } + + size_t offset = 0; + while (offset < event_len) { + struct inotify_event* pevent = reinterpret_cast(buf.data() + offset); + if (offset + sizeof(inotify_event) + pevent->len > event_len) { + // The pevent->len is too large and buffer will over flow. + // In general, should not happen, just make more stable. + return -1; + } + offset += sizeof(inotify_event) + pevent->len; + + pevent->name[pevent->len] = '\0'; + if (strncmp(pevent->name, "event", 5)) { + continue; + } + + android::base::unique_fd dfd(openat(dirfd(dir.get()), pevent->name, O_RDONLY)); + if (dfd == -1) { + break; + } + + if (!should_add_input_device(dfd, g_allow_touch_inputs)) { + continue; + } + + // Only add, we assume the user will not plug out and plug in USB device again and again :) + ev_add_fd(std::move(dfd), g_saved_input_cb); + } + + return 0; +} + int ev_init(ev_callback input_cb, bool allow_touch_inputs) { g_epoll_fd.reset(); @@ -68,7 +145,16 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) { return -1; } - std::unique_ptr dir(opendir("/dev/input"), closedir); + android::base::unique_fd inotify_fd(inotify_init1(IN_CLOEXEC)); + if (inotify_fd.get() == -1) { + return -1; + } + + if (inotify_add_watch(inotify_fd, INPUT_DEV_DIR, IN_CREATE) < 0) { + return -1; + } + + std::unique_ptr dir(opendir(INPUT_DEV_DIR), closedir); if (!dir) { return -1; } @@ -80,22 +166,10 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) { android::base::unique_fd fd(openat(dirfd(dir.get()), de->d_name, O_RDONLY | O_CLOEXEC)); if (fd == -1) continue; - // Use unsigned long to match ioctl's parameter type. - unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT - - // Read the evbits of the input device. - if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + if (!should_add_input_device(fd, allow_touch_inputs)) { continue; } - // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also - // allowed if allow_touch_inputs is set. - if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) { - if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) { - continue; - } - } - epoll_event ev; ev.events = EPOLLIN | EPOLLWAKEUP; ev.data.ptr = &ev_fdinfo[g_ev_count]; @@ -116,6 +190,11 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) { } g_epoll_fd.reset(epoll_fd.release()); + + g_saved_input_cb = input_cb; + g_allow_touch_inputs = allow_touch_inputs; + ev_add_fd(std::move(inotify_fd), inotify_cb); + return 0; } @@ -148,6 +227,7 @@ void ev_exit(void) { } g_ev_misc_count = 0; g_ev_dev_count = 0; + g_saved_input_cb = nullptr; g_epoll_fd.reset(); } @@ -170,13 +250,17 @@ void ev_dispatch(void) { } int ev_get_input(int fd, uint32_t epevents, input_event* ev) { - if (epevents & EPOLLIN) { - ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev))); - if (r == sizeof(*ev)) { - return 0; - } + if (epevents & EPOLLIN) { + ssize_t r = TEMP_FAILURE_RETRY(read(fd, ev, sizeof(*ev))); + if (r == sizeof(*ev)) { + return 0; } - return -1; + } + if (epevents & EPOLLHUP) { + // Delete this watch + epoll_ctl(g_epoll_fd, EPOLL_CTL_DEL, fd, nullptr); + } + return -1; } int ev_sync_key_state(const ev_set_key_callback& set_key_cb) { -- cgit v1.2.3