Merge "logcat: -v thread help missing"
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 23f6580..808d8ff 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1241,10 +1241,7 @@
         return true;
     });
 
-    D("update_transport_status: transports_ready = %s", result ? "true" : "false");
-
     bool ready;
-
     {
         std::lock_guard<std::mutex> lock(init_mutex);
         transports_ready = result;
@@ -1252,14 +1249,11 @@
     }
 
     if (ready) {
-        D("update_transport_status: notifying");
         init_cv.notify_all();
     }
 }
 
 void adb_notify_device_scan_complete() {
-    D("device scan complete");
-
     {
         std::lock_guard<std::mutex> lock(init_mutex);
         device_scan_complete = true;
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 18a8ff2..fc32469 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -151,10 +151,7 @@
 static auto& usb_handles = *new std::unordered_map<std::string, std::unique_ptr<usb_handle>>();
 static auto& usb_handles_mutex = *new std::mutex();
 
-static std::thread* device_poll_thread = nullptr;
-static bool terminate_device_poll_thread = false;
-static auto& device_poll_mutex = *new std::mutex();
-static auto& device_poll_cv = *new std::condition_variable();
+static libusb_hotplug_callback_handle hotplug_handle;
 
 static std::string get_device_address(libusb_device* device) {
     return StringPrintf("usb:%d:%d", libusb_get_bus_number(device),
@@ -175,6 +172,17 @@
     path += "/serial";
     return path;
 }
+
+static std::string get_device_dev_path(libusb_device* device) {
+    uint8_t ports[7];
+    int port_count = libusb_get_port_numbers(device, ports, 7);
+    if (port_count < 0) return "";
+    return StringPrintf("/dev/bus/usb/%03u/%03u", libusb_get_bus_number(device), ports[0]);
+}
+
+static bool is_device_accessible(libusb_device* device) {
+    return access(get_device_dev_path(device).c_str(), R_OK | W_OK) == 0;
+}
 #endif
 
 static bool endpoint_is_output(uint8_t endpoint) {
@@ -229,7 +237,7 @@
             // TODO: Is this assumption valid?
             LOG(VERBOSE) << "skipping interface with incorrect num_altsetting at " << device_address
                          << " (interface " << interface_num << ")";
-            return;
+            continue;
         }
 
         const libusb_interface_descriptor& interface_desc = interface.altsetting[0];
@@ -237,7 +245,7 @@
                               interface_desc.bInterfaceProtocol)) {
             LOG(VERBOSE) << "skipping non-adb interface at " << device_address << " (interface "
                          << interface_num << ")";
-            return;
+            continue;
         }
 
         LOG(VERBOSE) << "found potential adb interface at " << device_address << " (interface "
@@ -253,7 +261,7 @@
             const uint8_t transfer_type = endpoint_attr & LIBUSB_TRANSFER_TYPE_MASK;
 
             if (transfer_type != LIBUSB_TRANSFER_TYPE_BULK) {
-                return;
+                continue;
             }
 
             if (endpoint_is_output(endpoint_addr) && !found_out) {
@@ -371,33 +379,62 @@
     }
 
     register_usb_transport(usb_handle_raw, device_serial.c_str(), device_address.c_str(), writable);
-
     LOG(INFO) << "registered new usb device '" << device_serial << "'";
 }
 
-static void poll_for_devices() {
-    libusb_device** list;
-    adb_thread_setname("device poll");
-    while (true) {
-        const ssize_t device_count = libusb_get_device_list(nullptr, &list);
+static std::atomic<int> connecting_devices(0);
 
-        LOG(VERBOSE) << "found " << device_count << " attached devices";
-
-        for (ssize_t i = 0; i < device_count; ++i) {
-            process_device(list[i]);
+static void device_connected(libusb_device* device) {
+#if defined(__linux__)
+    // 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 checking for accessibility in a loop.
+    ++connecting_devices;
+    auto thread = std::thread([device]() {
+        std::string device_path = get_device_dev_path(device);
+        auto start = std::chrono::steady_clock::now();
+        while (std::chrono::steady_clock::now() - start < 500ms) {
+            if (is_device_accessible(device)) {
+                break;
+            }
+            std::this_thread::sleep_for(10ms);
         }
 
-        libusb_free_device_list(list, 1);
+        process_device(device);
+        --connecting_devices;
+    });
+    thread.detach();
+#else
+    process_device(device);
+#endif
+}
 
-        adb_notify_device_scan_complete();
+static void device_disconnected(libusb_device* device) {
+    std::string device_address = get_device_address(device);
 
-        std::unique_lock<std::mutex> lock(device_poll_mutex);
-        if (device_poll_cv.wait_for(lock, 500ms, []() { return terminate_device_poll_thread; })) {
-            return;
+    LOG(INFO) << "device disconnected: " << device_address;
+    std::unique_lock<std::mutex> lock(usb_handles_mutex);
+    auto it = usb_handles.find(device_address);
+    if (it != usb_handles.end()) {
+        if (!it->second->device_handle) {
+            // If the handle is null, we were never able to open the device.
+            unregister_usb_transport(it->second.get());
         }
+        usb_handles.erase(it);
     }
 }
 
+static int hotplug_callback(libusb_context*, libusb_device* device, libusb_hotplug_event event,
+                            void*) {
+    if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+        device_connected(device);
+    } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
+        device_disconnected(device);
+    }
+    return 0;
+}
+
 void usb_init() {
     LOG(DEBUG) << "initializing libusb...";
     int rc = libusb_init(nullptr);
@@ -405,6 +442,24 @@
         LOG(FATAL) << "failed to initialize libusb: " << libusb_error_name(rc);
     }
 
+    // Register the hotplug callback.
+    rc = libusb_hotplug_register_callback(
+        nullptr, static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED |
+                                                   LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT),
+        LIBUSB_HOTPLUG_ENUMERATE, LIBUSB_HOTPLUG_MATCH_ANY, LIBUSB_HOTPLUG_MATCH_ANY,
+        LIBUSB_CLASS_PER_INTERFACE, hotplug_callback, nullptr, &hotplug_handle);
+
+    if (rc != LIBUSB_SUCCESS) {
+        LOG(FATAL) << "failed to register libusb hotplug callback";
+    }
+
+    // Wait for all of the connecting devices to finish.
+    while (connecting_devices != 0) {
+        std::this_thread::sleep_for(10ms);
+    }
+
+    adb_notify_device_scan_complete();
+
     // Spawn a thread for libusb_handle_events.
     std::thread([]() {
         adb_thread_setname("libusb");
@@ -412,24 +467,10 @@
             libusb_handle_events(nullptr);
         }
     }).detach();
-
-    // Spawn a thread to do device enumeration.
-    // TODO: Use libusb_hotplug_* instead?
-    std::unique_lock<std::mutex> lock(device_poll_mutex);
-    device_poll_thread = new std::thread(poll_for_devices);
 }
 
 void usb_cleanup() {
-    {
-        std::unique_lock<std::mutex> lock(device_poll_mutex);
-        terminate_device_poll_thread = true;
-
-        if (!device_poll_thread) {
-            return;
-        }
-    }
-    device_poll_cv.notify_all();
-    device_poll_thread->join();
+    libusb_hotplug_deregister_callback(nullptr, hotplug_handle);
 }
 
 // Dispatch a libusb transfer, unlock |device_lock|, and then wait for the result.