From 723258a4c7d024473de8558c15a6db5437468681 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Wed, 10 May 2017 14:44:20 -0700 Subject: [PATCH 1/4] adb: silence noise. Remove logging statements that don't provide any benefit. Test: none Change-Id: Ib7c26fbdb019f4d6bbce2b7fb192cb5e6066e53f --- adb/adb.cpp | 6 ------ 1 file changed, 6 deletions(-) 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; From 6da1cd49b5eaeabb3febe584845de6bb3ae45873 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Fri, 12 May 2017 11:21:30 -0700 Subject: [PATCH 2/4] adb: libusb: switch to hotplug for device detection. Switch from polling in a loop to using libusb's hotplug API to detect when devices arrive and leave. Use this to remove devices that were inaccessible when they're unplugged. Bug: http://b/38170349 Test: plugged in device Change-Id: Id157412eb46834debecb0cd45b47b1ced50c2274 --- adb/client/usb_libusb.cpp | 72 +++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 37 deletions(-) diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp index 18a8ff23a..0068e084b 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), @@ -375,29 +372,31 @@ static void process_device(libusb_device* device) { 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 void remove_device(libusb_device* device) { + std::string device_address = get_device_address(device); - LOG(VERBOSE) << "found " << device_count << " attached devices"; - - for (ssize_t i = 0; i < device_count; ++i) { - process_device(list[i]); - } - - libusb_free_device_list(list, 1); - - adb_notify_device_scan_complete(); - - 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) { + process_device(device); + } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) { + remove_device(device); + } + return 0; +} + void usb_init() { LOG(DEBUG) << "initializing libusb..."; int rc = libusb_init(nullptr); @@ -405,6 +404,19 @@ 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"; + } + + adb_notify_device_scan_complete(); + // Spawn a thread for libusb_handle_events. std::thread([]() { adb_thread_setname("libusb"); @@ -412,24 +424,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. From 3f60a968e35b051943249fb9ac70e349323a0590 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Fri, 12 May 2017 14:46:50 -0700 Subject: [PATCH 3/4] adb: libusb: wait for devices to become accessible. 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. When detecting a new device, poll the device file for a while until we can access it, before trying to open it. Bug: http://b/38170349 Test: manually incrased timeout and chmodded a device betwen 0 and 664 Change-Id: I3c714f630940df02b407442592301e2bbb3d9653 --- adb/client/usb_libusb.cpp | 51 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp index 0068e084b..2508fc12e 100644 --- a/adb/client/usb_libusb.cpp +++ b/adb/client/usb_libusb.cpp @@ -172,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) { @@ -368,11 +379,38 @@ 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 remove_device(libusb_device* device) { +static std::atomic connecting_devices(0); + +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); + } + + process_device(device); + --connecting_devices; + }); + thread.detach(); +#else + process_device(device); +#endif +} + +static void device_disconnected(libusb_device* device) { std::string device_address = get_device_address(device); LOG(INFO) << "device disconnected: " << device_address; @@ -390,9 +428,9 @@ static void remove_device(libusb_device* device) { static int hotplug_callback(libusb_context*, libusb_device* device, libusb_hotplug_event event, void*) { if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) { - process_device(device); + device_connected(device); } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) { - remove_device(device); + device_disconnected(device); } return 0; } @@ -415,6 +453,11 @@ void usb_init() { 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. From 425aefdcf0f1b2068f56ca091047af5f02ae3f3d Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Fri, 12 May 2017 15:12:32 -0700 Subject: [PATCH 4/4] adb: libusb: recognize devices with multiple interfaces. A bug was introduced by commit 8bf37d7a wherein we accidentally only look at the first interface of a device when checking whether a USB device was an ADB device or not. Bug: http://b/38201318 Test: none Change-Id: I8e8e0963c77cd2cb03538d926ab735f4b57e52b7 --- adb/client/usb_libusb.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp index 2508fc12e..fc32469fa 100644 --- a/adb/client/usb_libusb.cpp +++ b/adb/client/usb_libusb.cpp @@ -237,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]; @@ -245,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 " @@ -261,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) {