adb: use a custom thread to poll for usb devices on mac.
On mac, if the adb server kicks a transport on some error, mac usb driver will not report a new usb device. So instead of relying on mac usb driver to report new usb devices, this CL uses a loop to search for usb devices not exist before. Note that this is also the behavior on windows and linux host. `adb reconnect` can be used to verity this CL. Bug: 25935458 Change-Id: I890e0eb1fae173f2e7a0c962ededa294d821e015
This commit is contained in:
parent
603df0e875
commit
48d4c0c42a
|
@ -613,7 +613,6 @@ static void transport_unref(atransport* t) {
|
|||
t->ref_count--;
|
||||
if (t->ref_count == 0) {
|
||||
D("transport: %s unref (kicking and closing)", t->serial);
|
||||
t->Kick();
|
||||
t->close(t);
|
||||
remove_transport(t);
|
||||
} else {
|
||||
|
|
272
adb/usb_osx.cpp
272
adb/usb_osx.cpp
|
@ -29,86 +29,96 @@
|
|||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include "adb.h"
|
||||
#include "transport.h"
|
||||
|
||||
#define DBG D
|
||||
|
||||
// There's no strerror equivalent for the errors returned by IOKit.
|
||||
// https://developer.apple.com/library/mac/documentation/DeviceDrivers/Conceptual/AccessingHardware/AH_Handling_Errors/AH_Handling_Errors.html
|
||||
// Search the web for "IOReturn.h" to find a complete up to date list.
|
||||
|
||||
static IONotificationPortRef notificationPort = 0;
|
||||
static io_iterator_t notificationIterator;
|
||||
|
||||
struct usb_handle
|
||||
{
|
||||
UInt8 bulkIn;
|
||||
UInt8 bulkOut;
|
||||
IOUSBInterfaceInterface190** interface;
|
||||
io_object_t usbNotification;
|
||||
unsigned int zero_mask;
|
||||
|
||||
// For garbage collecting disconnected devices.
|
||||
bool mark;
|
||||
std::string devpath;
|
||||
std::atomic<bool> dead;
|
||||
|
||||
usb_handle() : bulkIn(0), bulkOut(0), interface(nullptr),
|
||||
zero_mask(0), mark(false), dead(false) {
|
||||
}
|
||||
};
|
||||
|
||||
static CFRunLoopRef currentRunLoop = 0;
|
||||
static pthread_mutex_t start_lock;
|
||||
static pthread_cond_t start_cond;
|
||||
static std::atomic<bool> usb_inited_flag;
|
||||
|
||||
static auto& g_usb_handles_mutex = *new std::mutex();
|
||||
static auto& g_usb_handles = *new std::vector<std::unique_ptr<usb_handle>>();
|
||||
|
||||
static void AndroidInterfaceAdded(void *refCon, io_iterator_t iterator);
|
||||
static void AndroidInterfaceNotify(void *refCon, io_iterator_t iterator,
|
||||
natural_t messageType,
|
||||
void *messageArgument);
|
||||
static usb_handle* CheckInterface(IOUSBInterfaceInterface190 **iface,
|
||||
UInt16 vendor, UInt16 product);
|
||||
|
||||
static int
|
||||
InitUSB()
|
||||
{
|
||||
CFMutableDictionaryRef matchingDict;
|
||||
CFRunLoopSourceRef runLoopSource;
|
||||
|
||||
//* To set up asynchronous notifications, create a notification port and
|
||||
//* add its run loop event source to the program's run loop
|
||||
notificationPort = IONotificationPortCreate(kIOMasterPortDefault);
|
||||
runLoopSource = IONotificationPortGetRunLoopSource(notificationPort);
|
||||
CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopDefaultMode);
|
||||
|
||||
//* Create our matching dictionary to find the Android device's
|
||||
//* adb interface
|
||||
//* IOServiceAddMatchingNotification consumes the reference, so we do
|
||||
//* not need to release this
|
||||
matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
|
||||
|
||||
if (!matchingDict) {
|
||||
LOG(ERROR) << "Couldn't create USB matching dictionary.";
|
||||
return -1;
|
||||
static bool IsKnownDevice(const std::string& devpath) {
|
||||
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
|
||||
for (auto& usb : g_usb_handles) {
|
||||
if (usb->devpath == devpath) {
|
||||
// Set mark flag to indicate this device is still alive.
|
||||
usb->mark = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//* We have to get notifications for all potential candidates and test them
|
||||
//* at connection time because the matching rules don't allow for a
|
||||
//* USB interface class of 0xff for class+subclass+protocol matches
|
||||
//* See https://developer.apple.com/library/mac/qa/qa1076/_index.html
|
||||
IOServiceAddMatchingNotification(
|
||||
notificationPort,
|
||||
kIOFirstMatchNotification,
|
||||
matchingDict,
|
||||
AndroidInterfaceAdded,
|
||||
NULL,
|
||||
¬ificationIterator);
|
||||
static void usb_kick_locked(usb_handle* handle);
|
||||
|
||||
//* Iterate over set of matching interfaces to access already-present
|
||||
//* devices and to arm the notification
|
||||
AndroidInterfaceAdded(NULL, notificationIterator);
|
||||
static void KickDisconnectedDevices() {
|
||||
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
|
||||
for (auto& usb : g_usb_handles) {
|
||||
if (!usb->mark) {
|
||||
usb_kick_locked(usb.get());
|
||||
} else {
|
||||
usb->mark = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
static void AddDevice(std::unique_ptr<usb_handle> handle) {
|
||||
handle->mark = true;
|
||||
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
|
||||
g_usb_handles.push_back(std::move(handle));
|
||||
}
|
||||
|
||||
static void AndroidInterfaceAdded(io_iterator_t iterator);
|
||||
static std::unique_ptr<usb_handle> CheckInterface(IOUSBInterfaceInterface190 **iface,
|
||||
UInt16 vendor, UInt16 product);
|
||||
|
||||
static bool FindUSBDevices() {
|
||||
// Create the matching dictionary to find the Android device's adb interface.
|
||||
CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBInterfaceClassName);
|
||||
if (!matchingDict) {
|
||||
LOG(ERROR) << "couldn't create USB matching dictionary";
|
||||
return false;
|
||||
}
|
||||
// Create an iterator for all I/O Registry objects that match the dictionary.
|
||||
io_iterator_t iter = 0;
|
||||
kern_return_t kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
LOG(ERROR) << "failed to get matching services";
|
||||
return false;
|
||||
}
|
||||
// Iterate over all matching objects.
|
||||
AndroidInterfaceAdded(iter);
|
||||
IOObjectRelease(iter);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
|
||||
AndroidInterfaceAdded(io_iterator_t iterator)
|
||||
{
|
||||
kern_return_t kr;
|
||||
io_service_t usbDevice;
|
||||
|
@ -124,8 +134,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
|
|||
UInt16 product;
|
||||
UInt8 serialIndex;
|
||||
char serial[256];
|
||||
char devpathBuf[64];
|
||||
char *devpath = NULL;
|
||||
std::string devpath;
|
||||
|
||||
while ((usbInterface = IOIteratorNext(iterator))) {
|
||||
//* Create an intermediate interface plugin
|
||||
|
@ -199,9 +208,11 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
|
|||
kr = (*dev)->GetDeviceVendor(dev, &vendor);
|
||||
kr = (*dev)->GetDeviceProduct(dev, &product);
|
||||
kr = (*dev)->GetLocationID(dev, &locationId);
|
||||
if (kr == 0) {
|
||||
snprintf(devpathBuf, sizeof(devpathBuf), "usb:%" PRIu32 "X", locationId);
|
||||
devpath = devpathBuf;
|
||||
if (kr == KERN_SUCCESS) {
|
||||
devpath = android::base::StringPrintf("usb:%" PRIu32 "X", locationId);
|
||||
if (IsKnownDevice(devpath)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
|
||||
|
||||
|
@ -256,49 +267,29 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
|
|||
|
||||
(*dev)->Release(dev);
|
||||
|
||||
LOG(INFO) << android::base::StringPrintf("Found vid=%04x pid=%04x serial=%s\n",
|
||||
VLOG(USB) << android::base::StringPrintf("Found vid=%04x pid=%04x serial=%s\n",
|
||||
vendor, product, serial);
|
||||
|
||||
usb_handle* handle = CheckInterface((IOUSBInterfaceInterface190**)iface,
|
||||
vendor, product);
|
||||
if (handle == NULL) {
|
||||
LOG(ERROR) << "Could not find device interface";
|
||||
if (devpath.empty()) {
|
||||
devpath = serial;
|
||||
}
|
||||
if (IsKnownDevice(devpath)) {
|
||||
(*iface)->USBInterfaceClose(iface);
|
||||
(*iface)->Release(iface);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "AndroidDeviceAdded calling register_usb_transport";
|
||||
register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 1);
|
||||
|
||||
// Register for an interest notification of this device being removed.
|
||||
// Pass the reference to our private data as the refCon for the
|
||||
// notification.
|
||||
kr = IOServiceAddInterestNotification(notificationPort,
|
||||
usbInterface,
|
||||
kIOGeneralInterest,
|
||||
AndroidInterfaceNotify,
|
||||
handle,
|
||||
&handle->usbNotification);
|
||||
|
||||
if (kIOReturnSuccess != kr) {
|
||||
LOG(ERROR) << "Unable to create interest notification (" << std::hex << kr << ")";
|
||||
std::unique_ptr<usb_handle> handle = CheckInterface((IOUSBInterfaceInterface190**)iface,
|
||||
vendor, product);
|
||||
if (handle == nullptr) {
|
||||
LOG(ERROR) << "Could not find device interface";
|
||||
(*iface)->Release(iface);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
AndroidInterfaceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
|
||||
{
|
||||
usb_handle *handle = (usb_handle *)refCon;
|
||||
|
||||
if (messageType == kIOMessageServiceIsTerminated) {
|
||||
if (!handle) {
|
||||
LOG(ERROR) << "NULL handle";
|
||||
return;
|
||||
}
|
||||
LOG(DEBUG) << "AndroidInterfaceNotify";
|
||||
IOObjectRelease(handle->usbNotification);
|
||||
usb_kick(handle);
|
||||
handle->devpath = devpath;
|
||||
usb_handle* handle_p = handle.get();
|
||||
VLOG(USB) << "Add usb device " << serial;
|
||||
AddDevice(std::move(handle));
|
||||
register_usb_transport(handle_p, serial, devpath.c_str(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -316,10 +307,10 @@ static bool ClearPipeStallBothEnds(IOUSBInterfaceInterface190** interface, UInt8
|
|||
|
||||
//* TODO: simplify this further since we only register to get ADB interface
|
||||
//* subclass+protocol events
|
||||
static usb_handle*
|
||||
static std::unique_ptr<usb_handle>
|
||||
CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 product)
|
||||
{
|
||||
usb_handle* handle;
|
||||
std::unique_ptr<usb_handle> handle;
|
||||
IOReturn kr;
|
||||
UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
|
||||
UInt8 endpoint;
|
||||
|
@ -353,8 +344,10 @@ CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 pro
|
|||
goto err_bad_adb_interface;
|
||||
}
|
||||
|
||||
handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
|
||||
if (handle == nullptr) goto err_bad_adb_interface;
|
||||
handle.reset(new usb_handle);
|
||||
if (handle == nullptr) {
|
||||
goto err_bad_adb_interface;
|
||||
}
|
||||
|
||||
//* Iterate over the endpoints for this interface and find the first
|
||||
//* bulk in/out pipes available. These will be our read/write pipes.
|
||||
|
@ -392,39 +385,37 @@ CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 pro
|
|||
return handle;
|
||||
|
||||
err_get_pipe_props:
|
||||
free(handle);
|
||||
err_bad_adb_interface:
|
||||
err_get_interface_class:
|
||||
err_get_num_ep:
|
||||
(*interface)->USBInterfaceClose(interface);
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::mutex& operate_device_lock = *new std::mutex();
|
||||
|
||||
static void RunLoopThread(void* unused) {
|
||||
adb_thread_setname("RunLoop");
|
||||
InitUSB();
|
||||
|
||||
currentRunLoop = CFRunLoopGetCurrent();
|
||||
|
||||
// Signal the parent that we are running
|
||||
adb_mutex_lock(&start_lock);
|
||||
adb_cond_signal(&start_cond);
|
||||
adb_mutex_unlock(&start_lock);
|
||||
|
||||
CFRunLoopRun();
|
||||
currentRunLoop = 0;
|
||||
|
||||
IOObjectRelease(notificationIterator);
|
||||
IONotificationPortDestroy(notificationPort);
|
||||
|
||||
LOG(DEBUG) << "RunLoopThread done";
|
||||
VLOG(USB) << "RunLoopThread started";
|
||||
while (true) {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock_guard(operate_device_lock);
|
||||
FindUSBDevices();
|
||||
KickDisconnectedDevices();
|
||||
}
|
||||
// Signal the parent that we are running
|
||||
usb_inited_flag = true;
|
||||
adb_sleep_ms(1000);
|
||||
}
|
||||
VLOG(USB) << "RunLoopThread done";
|
||||
}
|
||||
|
||||
static void usb_cleanup() {
|
||||
LOG(DEBUG) << "usb_cleanup";
|
||||
VLOG(USB) << "usb_cleanup";
|
||||
// Wait until usb operations in RunLoopThread finish, and prevent further operations.
|
||||
operate_device_lock.lock();
|
||||
close_usb_devices();
|
||||
if (currentRunLoop)
|
||||
CFRunLoopStop(currentRunLoop);
|
||||
}
|
||||
|
||||
void usb_init() {
|
||||
|
@ -432,20 +423,16 @@ void usb_init() {
|
|||
if (!initialized) {
|
||||
atexit(usb_cleanup);
|
||||
|
||||
adb_mutex_init(&start_lock, NULL);
|
||||
adb_cond_init(&start_cond, NULL);
|
||||
usb_inited_flag = false;
|
||||
|
||||
if (!adb_thread_create(RunLoopThread, nullptr)) {
|
||||
fatal_errno("cannot create RunLoop thread");
|
||||
}
|
||||
|
||||
// Wait for initialization to finish
|
||||
adb_mutex_lock(&start_lock);
|
||||
adb_cond_wait(&start_cond, &start_lock);
|
||||
adb_mutex_unlock(&start_lock);
|
||||
|
||||
adb_mutex_destroy(&start_lock);
|
||||
adb_cond_destroy(&start_cond);
|
||||
while (!usb_inited_flag) {
|
||||
adb_sleep_ms(100);
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
@ -458,7 +445,7 @@ int usb_write(usb_handle *handle, const void *buf, int len)
|
|||
if (!len)
|
||||
return 0;
|
||||
|
||||
if (!handle)
|
||||
if (!handle || handle->dead)
|
||||
return -1;
|
||||
|
||||
if (NULL == handle->interface) {
|
||||
|
@ -499,7 +486,7 @@ int usb_read(usb_handle *handle, void *buf, int len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!handle) {
|
||||
if (!handle || handle->dead) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -532,20 +519,33 @@ int usb_read(usb_handle *handle, void *buf, int len)
|
|||
|
||||
int usb_close(usb_handle *handle)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
|
||||
for (auto it = g_usb_handles.begin(); it != g_usb_handles.end(); ++it) {
|
||||
if ((*it).get() == handle) {
|
||||
g_usb_handles.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle *handle)
|
||||
static void usb_kick_locked(usb_handle *handle)
|
||||
{
|
||||
LOG(INFO) << "Kicking handle";
|
||||
/* release the interface */
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
if (handle->interface)
|
||||
if (!handle->dead)
|
||||
{
|
||||
handle->dead = true;
|
||||
(*handle->interface)->USBInterfaceClose(handle->interface);
|
||||
(*handle->interface)->Release(handle->interface);
|
||||
handle->interface = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void usb_kick(usb_handle *handle) {
|
||||
// Use the lock to avoid multiple thread kicking the device at the same time.
|
||||
std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
|
||||
usb_kick_locked(handle);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue