init: Add inotify for /dev/input/

Since event sources can come and go asynchronously because of delayed
driver instantiation due to initialization or firmware upload, USB
attched devices, kernel module loads, or test automation sources like
monkey, add in inotify on /dev/input/ to support these possibilities.

Test: manual, boot, check registered chord works
Bug: 64114943
Change-Id: Ie598bb6f5bf94b2034ab33cf3be7fa15d3467141
diff --git a/init/keychords.cpp b/init/keychords.cpp
index f55d2c4..10c56e3 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -20,12 +20,14 @@
 #include <fcntl.h>
 #include <linux/input.h>
 #include <sys/cdefs.h>
+#include <sys/inotify.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <unistd.h>
 
 #include <algorithm>
 #include <functional>
+#include <map>
 #include <memory>
 #include <string>
 #include <vector>
@@ -118,6 +120,8 @@
 
 constexpr char kDevicePath[] = "/dev/input";
 
+std::map<std::string, int> keychord_registration;
+
 void HandleKeychord(int id) {
     // Only handle keychords if adb is enabled.
     std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
@@ -215,6 +219,7 @@
 }
 
 void GeteventOpenDevice(const std::string& device) {
+    if (keychord_registration.count(device)) return;
     auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
     if (fd == -1) {
         PLOG(ERROR) << "Can not open " << device;
@@ -222,21 +227,74 @@
     }
     if (!KeychordGeteventEnable(fd)) {
         ::close(fd);
+    } else {
+        keychord_registration.emplace(device, fd);
+    }
+}
+
+void GeteventCloseDevice(const std::string& device) {
+    auto it = keychord_registration.find(device);
+    if (it == keychord_registration.end()) return;
+    auto fd = (*it).second;
+    unregister_epoll_handler(fd);
+    keychord_registration.erase(it);
+    ::close(fd);
+}
+
+int inotify_fd = -1;
+
+void InotifyHandler() {
+    unsigned char buf[512];
+
+    auto res = TEMP_FAILURE_RETRY(::read(inotify_fd, buf, sizeof(buf)));
+    if (res < 0) {
+        PLOG(WARNING) << "could not get event";
+        return;
+    }
+
+    auto event_buf = buf;
+    while (static_cast<size_t>(res) >= sizeof(inotify_event)) {
+        auto event = reinterpret_cast<inotify_event*>(event_buf);
+        auto event_size = sizeof(inotify_event) + event->len;
+        if (static_cast<size_t>(res) < event_size) break;
+        if (event->len) {
+            std::string devname(kDevicePath);
+            devname += '/';
+            devname += event->name;
+            if (event->mask & IN_CREATE) {
+                GeteventOpenDevice(devname);
+            } else {
+                GeteventCloseDevice(devname);
+            }
+        }
+        res -= event_size;
+        event_buf += event_size;
     }
 }
 
 void GeteventOpenDevice() {
-    std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
-    if (!device) return;
-
-    dirent* entry;
-    while ((entry = readdir(device.get()))) {
-        if (entry->d_name[0] == '.') continue;
-        std::string devname(kDevicePath);
-        devname += '/';
-        devname += entry->d_name;
-        GeteventOpenDevice(devname);
+    inotify_fd = ::inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+    if (inotify_fd < 0) {
+        PLOG(WARNING) << "Could not instantiate inotify for " << kDevicePath;
+    } else if (::inotify_add_watch(inotify_fd, kDevicePath, IN_DELETE | IN_CREATE) < 0) {
+        PLOG(WARNING) << "Could not add watch for " << kDevicePath;
+        ::close(inotify_fd);
+        inotify_fd = -1;
     }
+
+    std::unique_ptr<DIR, decltype(&closedir)> device(opendir(kDevicePath), closedir);
+    if (device) {
+        dirent* entry;
+        while ((entry = readdir(device.get()))) {
+            if (entry->d_name[0] == '.') continue;
+            std::string devname(kDevicePath);
+            devname += '/';
+            devname += entry->d_name;
+            GeteventOpenDevice(devname);
+        }
+    }
+
+    if (inotify_fd >= 0) register_epoll_handler(inotify_fd, InotifyHandler);
 }
 
 void AddServiceKeycodes(Service* svc) {