Use inotify to wait for device to be accessible on Linux.

Bug: https://issuetracker.google.com/issues/192665697
Test: treehugger
Change-Id: Iab173898b1ebc72bf0a3322a293f2763b9686c04
diff --git a/client/usb_libusb.cpp b/client/usb_libusb.cpp
index b9ce1d1..4279b22 100644
--- a/client/usb_libusb.cpp
+++ b/client/usb_libusb.cpp
@@ -21,6 +21,11 @@
 #include <stdint.h>
 #include <stdlib.h>
 
+#if defined(__linux__)
+#include <sys/inotify.h>
+#include <unistd.h>
+#endif
+
 #include <atomic>
 #include <chrono>
 #include <condition_variable>
@@ -44,6 +49,8 @@
 #include "transfer_id.h"
 #include "transport.h"
 
+using namespace std::chrono_literals;
+
 using android::base::ScopedLockAssertion;
 using android::base::StringPrintf;
 
@@ -777,10 +784,60 @@
     // Android's host linux libusb uses netlink instead of udev for device hotplug notification,
     // which means we can get hotplug notifications before udev has updated ownership/perms on the
     // device. Since we're not going to be able to link against the system's libudev any time soon,
-    // hack around this by inserting a sleep.
+    // poll for accessibility changes with inotify until a timeout expires.
     libusb_ref_device(device);
     auto thread = std::thread([device]() {
-        std::this_thread::sleep_for(std::chrono::seconds(1));
+        std::string bus_path = StringPrintf("/dev/bus/usb/%03d/", libusb_get_bus_number(device));
+        std::string device_path =
+                StringPrintf("%s/%03d", bus_path.c_str(), libusb_get_device_address(device));
+        auto deadline = std::chrono::steady_clock::now() + 1s;
+        unique_fd infd(inotify_init1(IN_CLOEXEC | IN_NONBLOCK));
+        if (infd == -1) {
+            PLOG(FATAL) << "failed to create inotify fd";
+        }
+
+        // Register the watch first, and then check for accessibility, to avoid a race.
+        // We can't watch the device file itself, as that requires us to be able to access it.
+        if (inotify_add_watch(infd.get(), bus_path.c_str(), IN_ATTRIB) == -1) {
+            PLOG(ERROR) << "failed to register inotify watch on '" << bus_path
+                        << "', falling back to sleep";
+            std::this_thread::sleep_for(std::chrono::seconds(1));
+        } else {
+            adb_pollfd pfd = {.fd = infd.get(), .events = POLLIN, .revents = 0};
+
+            while (access(device_path.c_str(), R_OK | W_OK) == -1) {
+                auto timeout = deadline - std::chrono::steady_clock::now();
+                if (timeout < 0s) {
+                    break;
+                }
+
+                uint64_t ms = timeout / 1ms;
+                int rc = adb_poll(&pfd, 1, ms);
+                if (rc == -1) {
+                    if (errno == EINTR) {
+                        continue;
+                    } else {
+                        LOG(WARNING) << "timeout expired while waiting for device accessibility";
+                        break;
+                    }
+                }
+
+                union {
+                    struct inotify_event ev;
+                    char bytes[sizeof(struct inotify_event) + NAME_MAX + 1];
+                } buf;
+
+                rc = adb_read(infd.get(), &buf, sizeof(buf));
+                if (rc == -1) {
+                    break;
+                }
+
+                // We don't actually care about the data: we might get spurious events for
+                // other devices on the bus, but we'll double check in the loop condition.
+                continue;
+            }
+        }
+
         process_device(device);
         if (--connecting_devices == 0) {
             adb_notify_device_scan_complete();