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 <shuo.a.liu@intel.com>
Signed-off-by: Ming Tan <ming.tan@intel.com>
diff --git a/minui/events.cpp b/minui/events.cpp
index 7d0250e..f331ed6 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/epoll.h>
+#include <sys/inotify.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -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 @@
   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 @@
   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, decltype(&closedir)> dir(opendir(INPUT_DEV_DIR), closedir);
+  if (!dir) {
+    return -1;
+  }
+
+  std::vector<int8_t> 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<struct inotify_event*>(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 @@
     return -1;
   }
 
-  std::unique_ptr<DIR, decltype(&closedir)> 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, decltype(&closedir)> dir(opendir(INPUT_DEV_DIR), closedir);
   if (!dir) {
     return -1;
   }
@@ -80,22 +166,10 @@
     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 @@
   }
 
   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 @@
   }
   g_ev_misc_count = 0;
   g_ev_dev_count = 0;
+  g_saved_input_cb = nullptr;
   g_epoll_fd.reset();
 }
 
@@ -170,13 +250,17 @@
 }
 
 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) {