diff --git a/adb/adb.cpp b/adb/adb.cpp index 23f6580e7..808d8ff2e 100644 --- a/adb/adb.cpp +++ b/adb/adb.cpp @@ -1241,10 +1241,7 @@ void update_transport_status() { return true; }); - D("update_transport_status: transports_ready = %s", result ? "true" : "false"); - bool ready; - { std::lock_guard lock(init_mutex); transports_ready = result; @@ -1252,14 +1249,11 @@ void update_transport_status() { } if (ready) { - D("update_transport_status: notifying"); init_cv.notify_all(); } } void adb_notify_device_scan_complete() { - D("device scan complete"); - { std::lock_guard lock(init_mutex); device_scan_complete = true; diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp index 18a8ff23a..fc32469fa 100644 --- a/adb/client/usb_libusb.cpp +++ b/adb/client/usb_libusb.cpp @@ -151,10 +151,7 @@ struct usb_handle : public ::usb_handle { static auto& usb_handles = *new std::unordered_map>(); 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 @@ static std::string get_device_serial_path(libusb_device* device) { 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 @@ static void process_device(libusb_device* device) { // 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 @@ static void process_device(libusb_device* device) { 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 @@ static void process_device(libusb_device* device) { 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 @@ static void process_device(libusb_device* device) { } 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 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 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 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 @@ void usb_init() { 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_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 @@ void usb_init() { libusb_handle_events(nullptr); } }).detach(); - - // Spawn a thread to do device enumeration. - // TODO: Use libusb_hotplug_* instead? - std::unique_lock lock(device_poll_mutex); - device_poll_thread = new std::thread(poll_for_devices); } void usb_cleanup() { - { - std::unique_lock 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.