diff options
Diffstat (limited to 'device/usb/usb_service_impl.cc')
-rw-r--r-- | device/usb/usb_service_impl.cc | 614 |
1 files changed, 421 insertions, 193 deletions
diff --git a/device/usb/usb_service_impl.cc b/device/usb/usb_service_impl.cc index 6bdac2b..ce5b51a 100644 --- a/device/usb/usb_service_impl.cc +++ b/device/usb/usb_service_impl.cc @@ -4,6 +4,7 @@ #include "device/usb/usb_service_impl.h" +#include <algorithm> #include <set> #include "base/bind.h" @@ -11,25 +12,160 @@ #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" #include "base/thread_task_runner_handle.h" #include "components/device_event_log/device_event_log.h" #include "device/usb/usb_error.h" +#include "third_party/libusb/src/libusb/libusb.h" #if defined(OS_WIN) #include <setupapi.h> #include <usbiodef.h> -#include "base/scoped_observer.h" #include "base/strings/string_util.h" -#include "device/core/device_monitor_win.h" #endif // OS_WIN -namespace device { +#if defined(USE_UDEV) +#include "device/udev_linux/scoped_udev.h" +#endif // USE_UDEV -#if defined(OS_WIN) +namespace device { namespace { +#if defined(USE_UDEV) + +void ReadDeviceStrings(PlatformUsbDevice platform_device, + libusb_device_descriptor* descriptor, + base::string16* manufacturer_string, + base::string16* product_string, + base::string16* serial_number, + std::string* device_node) { + ScopedUdevPtr udev(udev_new()); + ScopedUdevEnumeratePtr enumerate(udev_enumerate_new(udev.get())); + + udev_enumerate_add_match_subsystem(enumerate.get(), "usb"); + if (udev_enumerate_scan_devices(enumerate.get()) != 0) { + return; + } + std::string bus_number = + base::IntToString(libusb_get_bus_number(platform_device)); + std::string device_address = + base::IntToString(libusb_get_device_address(platform_device)); + udev_list_entry* devices = udev_enumerate_get_list_entry(enumerate.get()); + for (udev_list_entry* i = devices; i != NULL; + i = udev_list_entry_get_next(i)) { + ScopedUdevDevicePtr device( + udev_device_new_from_syspath(udev.get(), udev_list_entry_get_name(i))); + if (device) { + const char* value = udev_device_get_sysattr_value(device.get(), "busnum"); + if (!value || bus_number != value) { + continue; + } + value = udev_device_get_sysattr_value(device.get(), "devnum"); + if (!value || device_address != value) { + continue; + } + + value = udev_device_get_devnode(device.get()); + if (value) { + *device_node = value; + } + value = udev_device_get_sysattr_value(device.get(), "manufacturer"); + if (value) { + *manufacturer_string = base::UTF8ToUTF16(value); + } + value = udev_device_get_sysattr_value(device.get(), "product"); + if (value) { + *product_string = base::UTF8ToUTF16(value); + } + value = udev_device_get_sysattr_value(device.get(), "serial"); + if (value) { + *serial_number = base::UTF8ToUTF16(value); + } + break; + } + } +} + +#else + +uint16 ReadDeviceLanguage(PlatformUsbDeviceHandle handle) { + uint16 language_id = 0x0409; + uint8 buffer[256]; + int size = + libusb_get_string_descriptor(handle, 0, 0, &buffer[0], sizeof(buffer)); + if (size < 0) { + USB_LOG(EVENT) << "Failed to get supported string languages: " + << ConvertPlatformUsbErrorToString(size); + } else if (size >= 4) { + // Just pick the first supported language. + language_id = buffer[2] | (buffer[3] << 8); + } else { + USB_LOG(EVENT) << "List of available string languages invalid."; + } + + return language_id; +} + +void ReadDeviceString(PlatformUsbDeviceHandle handle, + uint8 string_id, + uint16 language_id, + base::string16* string) { + if (string_id == 0) { + return; + } + + uint8 buffer[256]; + int size = libusb_get_string_descriptor(handle, string_id, language_id, + &buffer[0], sizeof(buffer)); + if (size < 0) { + USB_LOG(EVENT) << "Failed to read string " << (int)string_id + << " from the device: " + << ConvertPlatformUsbErrorToString(size); + } else if (size > 2) { + *string = base::string16(reinterpret_cast<base::char16*>(&buffer[2]), + size / 2 - 1); + } else { + USB_LOG(EVENT) << "String descriptor " << string_id << " is invalid."; + } +} + +void ReadDeviceStrings(PlatformUsbDevice platform_device, + libusb_device_descriptor* descriptor, + base::string16* manufacturer_string, + base::string16* product_string, + base::string16* serial_number, + std::string* device_node) { + if (descriptor->iManufacturer == 0 && descriptor->iProduct == 0 && + descriptor->iSerialNumber == 0) { + // Don't bother distrubing the device if it doesn't have any string + // descriptors we care about. + return; + } + + PlatformUsbDeviceHandle handle; + int rv = libusb_open(platform_device, &handle); + if (rv != LIBUSB_SUCCESS) { + USB_LOG(EVENT) << "Failed to open device to read string descriptors: " + << ConvertPlatformUsbErrorToString(rv); + return; + } + + uint16 language_id = ReadDeviceLanguage(handle); + ReadDeviceString(handle, descriptor->iManufacturer, language_id, + manufacturer_string); + ReadDeviceString(handle, descriptor->iProduct, language_id, product_string); + ReadDeviceString(handle, descriptor->iSerialNumber, language_id, + serial_number); + libusb_close(handle); +} + +#endif // USE_UDEV + +#if defined(OS_WIN) + // Wrapper around a HDEVINFO that automatically destroys it. class ScopedDeviceInfoList { public: @@ -81,62 +217,57 @@ class ScopedDeviceInfo { SP_DEVINFO_DATA dev_info_data_; }; -} // namespace - -// This class lives on the application main thread so that it can listen for -// device change notification window messages. It registers for notifications -// that may indicate new devices that the UsbService will enumerate. -class UsbServiceImpl::UIThreadHelper final - : private DeviceMonitorWin::Observer { - public: - UIThreadHelper(base::WeakPtr<UsbServiceImpl> usb_service) - : task_runner_(base::ThreadTaskRunnerHandle::Get()), - usb_service_(usb_service), - device_observer_(this) {} - - ~UIThreadHelper() {} +bool IsWinUsbInterface(const std::string& device_path) { + ScopedDeviceInfoList dev_info_list(SetupDiCreateDeviceInfoList(NULL, NULL)); + if (!dev_info_list.valid()) { + USB_PLOG(ERROR) << "Failed to create a device information set"; + return false; + } - void Start() { - DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces(); - if (device_monitor) { - device_observer_.Add(device_monitor); - } + // This will add the device to |dev_info_list| so we can query driver info. + if (!SetupDiOpenDeviceInterfaceA(dev_info_list.get(), device_path.c_str(), 0, + NULL)) { + USB_PLOG(ERROR) << "Failed to get device interface data for " + << device_path; + return false; } - private: - void OnDeviceAdded(const GUID& class_guid, - const std::string& device_path) override { - // Only the root node of a composite USB device has the class GUID - // GUID_DEVINTERFACE_USB_DEVICE but we want to wait until WinUSB is loaded. - // This first pass filter will catch anything that's sitting on the USB bus - // (including devices on 3rd party USB controllers) to avoid the more - // expensive driver check that needs to be done on the FILE thread. - if (device_path.find("usb") != std::string::npos) { - task_runner_->PostTask( - FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevicesIfWinUsbDevice, - usb_service_, device_path)); - } + ScopedDeviceInfo dev_info; + if (!SetupDiEnumDeviceInfo(dev_info_list.get(), 0, dev_info.get())) { + USB_PLOG(ERROR) << "Failed to get device info for " << device_path; + return false; } + dev_info.set_valid(dev_info_list.get()); - void OnDeviceRemoved(const GUID& class_guid, - const std::string& device_path) override { - // The root USB device node is removed last - if (class_guid == GUID_DEVINTERFACE_USB_DEVICE) { - task_runner_->PostTask( - FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevices, usb_service_)); - } + DWORD reg_data_type; + BYTE buffer[256]; + if (!SetupDiGetDeviceRegistryPropertyA(dev_info_list.get(), dev_info.get(), + SPDRP_SERVICE, ®_data_type, + &buffer[0], sizeof buffer, NULL)) { + USB_PLOG(ERROR) << "Failed to get device service property"; + return false; + } + if (reg_data_type != REG_SZ) { + USB_LOG(ERROR) << "Unexpected data type for driver service: " + << reg_data_type; + return false; } - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - base::WeakPtr<UsbServiceImpl> usb_service_; - ScopedObserver<DeviceMonitorWin, DeviceMonitorWin::Observer> device_observer_; -}; + USB_LOG(DEBUG) << "Driver for " << device_path << " is " << buffer << "."; + if (base::strncasecmp("WinUSB", (const char*)&buffer[0], sizeof "WinUSB") == + 0) { + return true; + } + return false; +} #endif // OS_WIN +} // namespace + // static UsbService* UsbServiceImpl::Create( - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) { + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) { PlatformUsbContext context = NULL; const int rv = libusb_init(&context); if (rv != LIBUSB_SUCCESS) { @@ -148,42 +279,21 @@ UsbService* UsbServiceImpl::Create( return nullptr; } - return new UsbServiceImpl(context, ui_task_runner); -} - -scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { - DCHECK(CalledOnValidThread()); - RefreshDevices(); - DeviceMap::iterator it = devices_.find(unique_id); - if (it != devices_.end()) { - return it->second; - } - return NULL; -} - -void UsbServiceImpl::GetDevices( - std::vector<scoped_refptr<UsbDevice> >* devices) { - DCHECK(CalledOnValidThread()); - STLClearObject(devices); - - if (!hotplug_enabled_) { - RefreshDevices(); - } - - for (const auto& map_entry : devices_) { - devices->push_back(map_entry.second); - } + return new UsbServiceImpl(context, blocking_task_runner); } UsbServiceImpl::UsbServiceImpl( PlatformUsbContext context, - scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner) + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) : context_(new UsbContext(context)), - ui_task_runner_(ui_task_runner), - next_unique_id_(0), - hotplug_enabled_(false), + task_runner_(base::ThreadTaskRunnerHandle::Get()), + blocking_task_runner_(blocking_task_runner), +#if defined(OS_WIN) + device_observer_(this), +#endif weak_factory_(this) { - task_runner_ = base::ThreadTaskRunnerHandle::Get(); + base::MessageLoop::current()->AddDestructionObserver(this); + int rv = libusb_hotplug_register_callback( context_->context(), static_cast<libusb_hotplug_event>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | @@ -193,149 +303,269 @@ UsbServiceImpl::UsbServiceImpl( &UsbServiceImpl::HotplugCallback, this, &hotplug_handle_); if (rv == LIBUSB_SUCCESS) { hotplug_enabled_ = true; + + // libusb will call the hotplug callback for each device currently + // enumerated. Once this is complete enumeration_ready_ can be set to true + // but we must first wait for any tasks posted to blocking_task_runner_ to + // complete. + blocking_task_runner_->PostTaskAndReply( + FROM_HERE, base::Bind(&base::DoNothing), + base::Bind(&UsbServiceImpl::RefreshDevicesComplete, + weak_factory_.GetWeakPtr(), nullptr, 0)); } else { + RefreshDevices(""); #if defined(OS_WIN) - ui_thread_helper_ = new UIThreadHelper(weak_factory_.GetWeakPtr()); - ui_task_runner_->PostTask(FROM_HERE, - base::Bind(&UIThreadHelper::Start, - base::Unretained(ui_thread_helper_))); + DeviceMonitorWin* device_monitor = DeviceMonitorWin::GetForAllInterfaces(); + if (device_monitor) { + device_observer_.Add(device_monitor); + } #endif // OS_WIN } } UsbServiceImpl::~UsbServiceImpl() { + base::MessageLoop::current()->RemoveDestructionObserver(this); + if (hotplug_enabled_) { libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_); } -#if defined(OS_WIN) - if (ui_thread_helper_) { - ui_task_runner_->DeleteSoon(FROM_HERE, ui_thread_helper_); - } -#endif // OS_WIN for (const auto& map_entry : devices_) { map_entry.second->OnDisconnect(); } } -void UsbServiceImpl::RefreshDevices() { +scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) { DCHECK(CalledOnValidThread()); - - libusb_device** platform_devices = NULL; - const ssize_t device_count = - libusb_get_device_list(context_->context(), &platform_devices); - if (device_count < 0) { - USB_LOG(ERROR) << "Failed to get device list: " - << ConvertPlatformUsbErrorToString(device_count); + DeviceMap::iterator it = devices_.find(unique_id); + if (it != devices_.end()) { + return it->second; } + return NULL; +} - std::set<UsbDevice*> connected_devices; - std::vector<PlatformUsbDevice> disconnected_devices; +void UsbServiceImpl::GetDevices(const GetDevicesCallback& callback) { + DCHECK(CalledOnValidThread()); - // Populates new devices. - for (ssize_t i = 0; i < device_count; ++i) { - if (!ContainsKey(platform_devices_, platform_devices[i])) { - scoped_refptr<UsbDeviceImpl> new_device = AddDevice(platform_devices[i]); - if (new_device) { - connected_devices.insert(new_device.get()); - } - } else { - connected_devices.insert(platform_devices_[platform_devices[i]].get()); + if (!enumeration_ready_) { + // On startup wait for the first enumeration, + pending_enumerations_.push_back(callback); + } else if (hotplug_enabled_) { + // The device list is updated live when hotplug events are supported. + std::vector<scoped_refptr<UsbDevice>> devices; + for (const auto& map_entry : devices_) { + devices.push_back(map_entry.second); + } + callback.Run(devices); + } else { + // Only post one re-enumeration task at a time. + if (pending_enumerations_.empty()) { + RefreshDevices(""); } + pending_enumerations_.push_back(callback); } +} - // Find disconnected devices. - for (const auto& map_entry : platform_devices_) { - PlatformUsbDevice platform_device = map_entry.first; - scoped_refptr<UsbDeviceImpl> device = map_entry.second; - if (!ContainsKey(connected_devices, device.get())) { - disconnected_devices.push_back(platform_device); - devices_.erase(device->unique_id()); - - NotifyDeviceRemoved(device); - device->OnDisconnect(); - } +#if defined(OS_WIN) + +void UsbServiceImpl::OnDeviceAdded(const GUID& class_guid, + const std::string& device_path) { + // Only the root node of a composite USB device has the class GUID + // GUID_DEVINTERFACE_USB_DEVICE but we want to wait until WinUSB is loaded. + // This first pass filter will catch anything that's sitting on the USB bus + // (including devices on 3rd party USB controllers) to avoid the more + // expensive driver check that needs to be done on the FILE thread. + if (device_path.find("usb") != std::string::npos) { + RefreshDevices(device_path); } +} - // Remove disconnected devices from platform_devices_. - for (const PlatformUsbDevice& platform_device : disconnected_devices) { - // UsbDevice will be destroyed after this. The corresponding - // PlatformUsbDevice will be unref'ed during this process. - platform_devices_.erase(platform_device); +void UsbServiceImpl::OnDeviceRemoved(const GUID& class_guid, + const std::string& device_path) { + // The root USB device node is removed last + if (class_guid == GUID_DEVINTERFACE_USB_DEVICE) { + RefreshDevices(""); } +} - libusb_free_device_list(platform_devices, true); +#endif // OS_WIN + +void UsbServiceImpl::WillDestroyCurrentMessageLoop() { + DCHECK(CalledOnValidThread()); + delete this; } -#if defined(OS_WIN) -void UsbServiceImpl::RefreshDevicesIfWinUsbDevice( - const std::string& device_path) { - ScopedDeviceInfoList dev_info_list(SetupDiCreateDeviceInfoList(NULL, NULL)); - if (!dev_info_list.valid()) { - USB_PLOG(ERROR) << "Failed to create a device information set"; - return; - } +void UsbServiceImpl::RefreshDevices(const std::string& new_device_path) { + DCHECK(CalledOnValidThread()); - // This will add the device to |dev_info_list| so we can query driver info. - if (!SetupDiOpenDeviceInterfaceA(dev_info_list.get(), device_path.c_str(), 0, - NULL)) { - USB_PLOG(ERROR) << "Failed to get device interface data for " - << device_path; - return; + std::set<PlatformUsbDevice> current_devices; + for (const auto& map_entry : platform_devices_) { + current_devices.insert(map_entry.first); } + blocking_task_runner_->PostTask( + FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevicesOnBlockingThread, + weak_factory_.GetWeakPtr(), new_device_path, + task_runner_, context_, current_devices)); +} - ScopedDeviceInfo dev_info; - if (!SetupDiEnumDeviceInfo(dev_info_list.get(), 0, dev_info.get())) { - USB_PLOG(ERROR) << "Failed to get device info for " << device_path; - return; +// static +void UsbServiceImpl::RefreshDevicesOnBlockingThread( + base::WeakPtr<UsbServiceImpl> usb_service, + const std::string& new_device_path, + scoped_refptr<base::SequencedTaskRunner> task_runner, + scoped_refptr<UsbContext> usb_context, + const std::set<PlatformUsbDevice>& previous_devices) { + if (!new_device_path.empty()) { +#if defined(OS_WIN) + if (!IsWinUsbInterface(new_device_path)) { + // Wait to call libusb_get_device_list until libusb will be able to find + // a WinUSB interface for the device. + return; + } +#endif // defined(OS_WIN) } - dev_info.set_valid(dev_info_list.get()); - DWORD reg_data_type; - BYTE buffer[256]; - if (!SetupDiGetDeviceRegistryPropertyA(dev_info_list.get(), dev_info.get(), - SPDRP_SERVICE, ®_data_type, - &buffer[0], sizeof buffer, NULL)) { - USB_PLOG(ERROR) << "Failed to get device service property"; - return; - } - if (reg_data_type != REG_SZ) { - USB_LOG(ERROR) << "Unexpected data type for driver service: " - << reg_data_type; + libusb_device** platform_devices = NULL; + const ssize_t device_count = + libusb_get_device_list(usb_context->context(), &platform_devices); + if (device_count < 0) { + USB_LOG(ERROR) << "Failed to get device list: " + << ConvertPlatformUsbErrorToString(device_count); + task_runner->PostTask(FROM_HERE, + base::Bind(&UsbServiceImpl::RefreshDevicesComplete, + usb_service, nullptr, 0)); return; } - USB_LOG(DEBUG) << "Driver for " << device_path << " is " << buffer << "."; - if (base::strncasecmp("WinUSB", (const char*)&buffer[0], sizeof "WinUSB") == - 0) { - RefreshDevices(); + // Find new devices. + for (ssize_t i = 0; i < device_count; ++i) { + PlatformUsbDevice platform_device = platform_devices[i]; + if (previous_devices.find(platform_device) == previous_devices.end()) { + libusb_ref_device(platform_device); + AddDeviceOnBlockingThread(usb_service, task_runner, platform_device); + } } + + // |platform_devices| will be freed in this callback. + task_runner->PostTask( + FROM_HERE, base::Bind(&UsbServiceImpl::RefreshDevicesComplete, + usb_service, platform_devices, device_count)); } -#endif // OS_WIN -scoped_refptr<UsbDeviceImpl> UsbServiceImpl::AddDevice( +// static +void UsbServiceImpl::AddDeviceOnBlockingThread( + base::WeakPtr<UsbServiceImpl> usb_service, + scoped_refptr<base::SequencedTaskRunner> task_runner, PlatformUsbDevice platform_device) { libusb_device_descriptor descriptor; int rv = libusb_get_device_descriptor(platform_device, &descriptor); if (rv == LIBUSB_SUCCESS) { - uint32 unique_id; - do { - unique_id = ++next_unique_id_; - } while (devices_.find(unique_id) != devices_.end()); - - scoped_refptr<UsbDeviceImpl> new_device(new UsbDeviceImpl( - context_, ui_task_runner_, platform_device, descriptor.idVendor, - descriptor.idProduct, unique_id)); - platform_devices_[platform_device] = new_device; - devices_[unique_id] = new_device; - NotifyDeviceAdded(new_device); - return new_device; + base::string16 manufacturer_string; + base::string16 product_string; + base::string16 serial_number; + std::string device_node; + ReadDeviceStrings(platform_device, &descriptor, &manufacturer_string, + &product_string, &serial_number, &device_node); + + task_runner->PostTask( + FROM_HERE, base::Bind(&UsbServiceImpl::AddDevice, usb_service, + platform_device, descriptor.idVendor, + descriptor.idProduct, manufacturer_string, + product_string, serial_number, device_node)); } else { USB_LOG(EVENT) << "Failed to get device descriptor: " << ConvertPlatformUsbErrorToString(rv); - return nullptr; + libusb_unref_device(platform_device); } } +void UsbServiceImpl::RefreshDevicesComplete(libusb_device** platform_devices, + ssize_t device_count) { + if (platform_devices) { + // Mark devices seen in this enumeration. + for (ssize_t i = 0; i < device_count; ++i) { + const PlatformDeviceMap::iterator it = + platform_devices_.find(platform_devices[i]); + if (it != platform_devices_.end()) { + it->second->set_visited(true); + } + } + + // Remove devices not seen in this enumeration. + for (PlatformDeviceMap::iterator it = platform_devices_.begin(); + it != platform_devices_.end(); + /* incremented internally */) { + PlatformDeviceMap::iterator current = it++; + const scoped_refptr<UsbDeviceImpl>& device = current->second; + if (device->was_visited()) { + device->set_visited(false); + } else { + RemoveDevice(device); + } + } + + libusb_free_device_list(platform_devices, true); + } + + enumeration_ready_ = true; + + if (!pending_enumerations_.empty()) { + std::vector<scoped_refptr<UsbDevice>> devices; + for (const auto& map_entry : devices_) { + devices.push_back(map_entry.second); + } + + std::vector<GetDevicesCallback> pending_enumerations; + pending_enumerations.swap(pending_enumerations_); + for (const GetDevicesCallback& callback : pending_enumerations) { + callback.Run(devices); + } + } +} + +void UsbServiceImpl::AddDevice(PlatformUsbDevice platform_device, + uint16 vendor_id, + uint16 product_id, + base::string16 manufacturer_string, + base::string16 product_string, + base::string16 serial_number, + std::string device_node) { + uint32 unique_id; + do { + unique_id = ++next_unique_id_; + } while (devices_.find(unique_id) != devices_.end()); + + scoped_refptr<UsbDeviceImpl> device( + new UsbDeviceImpl(context_, platform_device, vendor_id, product_id, + unique_id, manufacturer_string, product_string, + serial_number, blocking_task_runner_)); + + platform_devices_[platform_device] = device; + devices_[unique_id] = device; + + USB_LOG(USER) << "USB device added: vendor=" << device->vendor_id() << " \"" + << device->manufacturer_string() + << "\", product=" << device->product_id() << " \"" + << device->product_string() << "\", serial=\"" + << device->serial_number() + << "\", uniqueId=" << device->unique_id(); + + if (enumeration_ready_) { + NotifyDeviceAdded(device); + } + + libusb_unref_device(platform_device); +} + +void UsbServiceImpl::RemoveDevice(scoped_refptr<UsbDeviceImpl> device) { + platform_devices_.erase(device->platform_device()); + devices_.erase(device->unique_id()); + + USB_LOG(USER) << "USB device removed: uniqueId=" << device->unique_id(); + + NotifyDeviceRemoved(device); + device->OnDisconnect(); +} + // static int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, PlatformUsbDevice device, @@ -348,22 +578,22 @@ int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, UsbServiceImpl* self = reinterpret_cast<UsbServiceImpl*>(user_data); switch (event) { case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED: - libusb_ref_device(device); // Released in OnDeviceAdded. + libusb_ref_device(device); // Released in OnPlatformDeviceAdded. if (self->task_runner_->BelongsToCurrentThread()) { - self->OnDeviceAdded(device); + self->OnPlatformDeviceAdded(device); } else { self->task_runner_->PostTask( - FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceAdded, + FROM_HERE, base::Bind(&UsbServiceImpl::OnPlatformDeviceAdded, base::Unretained(self), device)); } break; case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT: - libusb_ref_device(device); // Released in OnDeviceRemoved. + libusb_ref_device(device); // Released in OnPlatformDeviceRemoved. if (self->task_runner_->BelongsToCurrentThread()) { - self->OnDeviceRemoved(device); + self->OnPlatformDeviceRemoved(device); } else { self->task_runner_->PostTask( - FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceRemoved, + FROM_HERE, base::Bind(&UsbServiceImpl::OnPlatformDeviceRemoved, base::Unretained(self), device)); } break; @@ -374,30 +604,28 @@ int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context, return 0; } -void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device) { +void UsbServiceImpl::OnPlatformDeviceAdded(PlatformUsbDevice platform_device) { DCHECK(CalledOnValidThread()); DCHECK(!ContainsKey(platform_devices_, platform_device)); + blocking_task_runner_->PostTask( + FROM_HERE, + base::Bind(&UsbServiceImpl::AddDeviceOnBlockingThread, + weak_factory_.GetWeakPtr(), task_runner_, platform_device)); - AddDevice(platform_device); - libusb_unref_device(platform_device); + // libusb_unref_device(platform_device) is called by the task above. } -void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device) { +void UsbServiceImpl::OnPlatformDeviceRemoved( + PlatformUsbDevice platform_device) { DCHECK(CalledOnValidThread()); - PlatformDeviceMap::iterator it = platform_devices_.find(platform_device); if (it != platform_devices_.end()) { scoped_refptr<UsbDeviceImpl> device = it->second; - DeviceMap::iterator dev_it = devices_.find(device->unique_id()); - if (dev_it != devices_.end()) { - devices_.erase(dev_it); - } else { - NOTREACHED(); - } - platform_devices_.erase(it); - - NotifyDeviceRemoved(device); - device->OnDisconnect(); + // Serialize with calls to AddDeviceOnBlockingThread. + blocking_task_runner_->PostTaskAndReply( + FROM_HERE, base::Bind(&base::DoNothing), + base::Bind(&UsbServiceImpl::RemoveDevice, weak_factory_.GetWeakPtr(), + device)); } else { NOTREACHED(); } |