Merge "adb: use a custom thread to poll for usb devices on mac."

This commit is contained in:
Yabin Cui 2016-04-19 20:22:13 +00:00 committed by Gerrit Code Review
commit 09c5fcf760
2 changed files with 136 additions and 137 deletions

View file

@ -613,7 +613,6 @@ static void transport_unref(atransport* t) {
t->ref_count--; t->ref_count--;
if (t->ref_count == 0) { if (t->ref_count == 0) {
D("transport: %s unref (kicking and closing)", t->serial); D("transport: %s unref (kicking and closing)", t->serial);
t->Kick();
t->close(t); t->close(t);
remove_transport(t); remove_transport(t);
} else { } else {

View file

@ -29,86 +29,96 @@
#include <inttypes.h> #include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <atomic>
#include <memory>
#include <mutex>
#include <vector>
#include <android-base/logging.h> #include <android-base/logging.h>
#include <android-base/stringprintf.h> #include <android-base/stringprintf.h>
#include "adb.h" #include "adb.h"
#include "transport.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 struct usb_handle
{ {
UInt8 bulkIn; UInt8 bulkIn;
UInt8 bulkOut; UInt8 bulkOut;
IOUSBInterfaceInterface190** interface; IOUSBInterfaceInterface190** interface;
io_object_t usbNotification;
unsigned int zero_mask; 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 std::atomic<bool> usb_inited_flag;
static pthread_mutex_t start_lock;
static pthread_cond_t start_cond;
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 bool IsKnownDevice(const std::string& devpath) {
static void AndroidInterfaceNotify(void *refCon, io_iterator_t iterator, std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
natural_t messageType, for (auto& usb : g_usb_handles) {
void *messageArgument); if (usb->devpath == devpath) {
static usb_handle* CheckInterface(IOUSBInterfaceInterface190 **iface, // Set mark flag to indicate this device is still alive.
UInt16 vendor, UInt16 product); usb->mark = true;
return true;
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;
} }
return false;
}
//* We have to get notifications for all potential candidates and test them static void usb_kick_locked(usb_handle* handle);
//* 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,
&notificationIterator);
//* Iterate over set of matching interfaces to access already-present static void KickDisconnectedDevices() {
//* devices and to arm the notification std::lock_guard<std::mutex> lock_guard(g_usb_handles_mutex);
AndroidInterfaceAdded(NULL, notificationIterator); 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 static void
AndroidInterfaceAdded(void *refCon, io_iterator_t iterator) AndroidInterfaceAdded(io_iterator_t iterator)
{ {
kern_return_t kr; kern_return_t kr;
io_service_t usbDevice; io_service_t usbDevice;
@ -124,8 +134,7 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
UInt16 product; UInt16 product;
UInt8 serialIndex; UInt8 serialIndex;
char serial[256]; char serial[256];
char devpathBuf[64]; std::string devpath;
char *devpath = NULL;
while ((usbInterface = IOIteratorNext(iterator))) { while ((usbInterface = IOIteratorNext(iterator))) {
//* Create an intermediate interface plugin //* Create an intermediate interface plugin
@ -199,9 +208,11 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
kr = (*dev)->GetDeviceVendor(dev, &vendor); kr = (*dev)->GetDeviceVendor(dev, &vendor);
kr = (*dev)->GetDeviceProduct(dev, &product); kr = (*dev)->GetDeviceProduct(dev, &product);
kr = (*dev)->GetLocationID(dev, &locationId); kr = (*dev)->GetLocationID(dev, &locationId);
if (kr == 0) { if (kr == KERN_SUCCESS) {
snprintf(devpathBuf, sizeof(devpathBuf), "usb:%" PRIu32 "X", locationId); devpath = android::base::StringPrintf("usb:%" PRIu32 "X", locationId);
devpath = devpathBuf; if (IsKnownDevice(devpath)) {
continue;
}
} }
kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex);
@ -256,49 +267,29 @@ AndroidInterfaceAdded(void *refCon, io_iterator_t iterator)
(*dev)->Release(dev); (*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); vendor, product, serial);
if (devpath.empty()) {
usb_handle* handle = CheckInterface((IOUSBInterfaceInterface190**)iface, devpath = serial;
vendor, product); }
if (handle == NULL) { if (IsKnownDevice(devpath)) {
LOG(ERROR) << "Could not find device interface"; (*iface)->USBInterfaceClose(iface);
(*iface)->Release(iface); (*iface)->Release(iface);
continue; continue;
} }
LOG(DEBUG) << "AndroidDeviceAdded calling register_usb_transport"; std::unique_ptr<usb_handle> handle = CheckInterface((IOUSBInterfaceInterface190**)iface,
register_usb_transport(handle, (serial[0] ? serial : NULL), devpath, 1); vendor, product);
if (handle == nullptr) {
// Register for an interest notification of this device being removed. LOG(ERROR) << "Could not find device interface";
// Pass the reference to our private data as the refCon for the (*iface)->Release(iface);
// notification. continue;
kr = IOServiceAddInterestNotification(notificationPort,
usbInterface,
kIOGeneralInterest,
AndroidInterfaceNotify,
handle,
&handle->usbNotification);
if (kIOReturnSuccess != kr) {
LOG(ERROR) << "Unable to create interest notification (" << std::hex << kr << ")";
} }
} handle->devpath = devpath;
} usb_handle* handle_p = handle.get();
VLOG(USB) << "Add usb device " << serial;
static void AddDevice(std::move(handle));
AndroidInterfaceNotify(void *refCon, io_service_t service, natural_t messageType, void *messageArgument) register_usb_transport(handle_p, serial, devpath.c_str(), 1);
{
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);
} }
} }
@ -316,10 +307,10 @@ static bool ClearPipeStallBothEnds(IOUSBInterfaceInterface190** interface, UInt8
//* TODO: simplify this further since we only register to get ADB interface //* TODO: simplify this further since we only register to get ADB interface
//* subclass+protocol events //* subclass+protocol events
static usb_handle* static std::unique_ptr<usb_handle>
CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 product) CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 product)
{ {
usb_handle* handle; std::unique_ptr<usb_handle> handle;
IOReturn kr; IOReturn kr;
UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol; UInt8 interfaceNumEndpoints, interfaceClass, interfaceSubClass, interfaceProtocol;
UInt8 endpoint; UInt8 endpoint;
@ -353,8 +344,10 @@ CheckInterface(IOUSBInterfaceInterface190 **interface, UInt16 vendor, UInt16 pro
goto err_bad_adb_interface; goto err_bad_adb_interface;
} }
handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle))); handle.reset(new usb_handle);
if (handle == nullptr) goto err_bad_adb_interface; if (handle == nullptr) {
goto err_bad_adb_interface;
}
//* Iterate over the endpoints for this interface and find the first //* Iterate over the endpoints for this interface and find the first
//* bulk in/out pipes available. These will be our read/write pipes. //* 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; return handle;
err_get_pipe_props: err_get_pipe_props:
free(handle);
err_bad_adb_interface: err_bad_adb_interface:
err_get_interface_class: err_get_interface_class:
err_get_num_ep: err_get_num_ep:
(*interface)->USBInterfaceClose(interface); (*interface)->USBInterfaceClose(interface);
return NULL; return nullptr;
} }
std::mutex& operate_device_lock = *new std::mutex();
static void RunLoopThread(void* unused) { static void RunLoopThread(void* unused) {
adb_thread_setname("RunLoop"); adb_thread_setname("RunLoop");
InitUSB();
currentRunLoop = CFRunLoopGetCurrent(); VLOG(USB) << "RunLoopThread started";
while (true) {
// Signal the parent that we are running {
adb_mutex_lock(&start_lock); std::lock_guard<std::mutex> lock_guard(operate_device_lock);
adb_cond_signal(&start_cond); FindUSBDevices();
adb_mutex_unlock(&start_lock); KickDisconnectedDevices();
}
CFRunLoopRun(); // Signal the parent that we are running
currentRunLoop = 0; usb_inited_flag = true;
adb_sleep_ms(1000);
IOObjectRelease(notificationIterator); }
IONotificationPortDestroy(notificationPort); VLOG(USB) << "RunLoopThread done";
LOG(DEBUG) << "RunLoopThread done";
} }
static void usb_cleanup() { 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(); close_usb_devices();
if (currentRunLoop)
CFRunLoopStop(currentRunLoop);
} }
void usb_init() { void usb_init() {
@ -432,20 +423,16 @@ void usb_init() {
if (!initialized) { if (!initialized) {
atexit(usb_cleanup); atexit(usb_cleanup);
adb_mutex_init(&start_lock, NULL); usb_inited_flag = false;
adb_cond_init(&start_cond, NULL);
if (!adb_thread_create(RunLoopThread, nullptr)) { if (!adb_thread_create(RunLoopThread, nullptr)) {
fatal_errno("cannot create RunLoop thread"); fatal_errno("cannot create RunLoop thread");
} }
// Wait for initialization to finish // Wait for initialization to finish
adb_mutex_lock(&start_lock); while (!usb_inited_flag) {
adb_cond_wait(&start_cond, &start_lock); adb_sleep_ms(100);
adb_mutex_unlock(&start_lock); }
adb_mutex_destroy(&start_lock);
adb_cond_destroy(&start_cond);
initialized = true; initialized = true;
} }
@ -458,7 +445,7 @@ int usb_write(usb_handle *handle, const void *buf, int len)
if (!len) if (!len)
return 0; return 0;
if (!handle) if (!handle || handle->dead)
return -1; return -1;
if (NULL == handle->interface) { if (NULL == handle->interface) {
@ -499,7 +486,7 @@ int usb_read(usb_handle *handle, void *buf, int len)
return 0; return 0;
} }
if (!handle) { if (!handle || handle->dead) {
return -1; return -1;
} }
@ -532,20 +519,33 @@ int usb_read(usb_handle *handle, void *buf, int len)
int usb_close(usb_handle *handle) 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; return 0;
} }
void usb_kick(usb_handle *handle) static void usb_kick_locked(usb_handle *handle)
{ {
LOG(INFO) << "Kicking handle"; LOG(INFO) << "Kicking handle";
/* release the interface */ /* release the interface */
if (!handle) if (!handle)
return; return;
if (handle->interface) if (!handle->dead)
{ {
handle->dead = true;
(*handle->interface)->USBInterfaceClose(handle->interface); (*handle->interface)->USBInterfaceClose(handle->interface);
(*handle->interface)->Release(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);
}