1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "device/usb/usb_service_impl.h"
10 #include "base/location.h"
11 #include "base/memory/weak_ptr.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/stl_util.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "device/usb/usb_error.h"
20 #include "base/scoped_observer.h"
21 #include "device/core/device_monitor_win.h"
27 // This class lives on the application main thread so that it can listen for
28 // device change notification window messages. It registers for notifications
29 // regarding devices implementating the "UsbDevice" interface, which represents
30 // most of the devices the UsbService will enumerate.
31 class UsbServiceImpl::UIThreadHelper final
32 : private DeviceMonitorWin::Observer
{
34 UIThreadHelper(base::WeakPtr
<UsbServiceImpl
> usb_service
)
35 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
36 usb_service_(usb_service
),
37 device_observer_(this) {}
42 DeviceMonitorWin
* device_monitor
=
43 DeviceMonitorWin::GetForDeviceInterface(GUID_DEVINTERFACE_USB_DEVICE
);
45 device_observer_
.Add(device_monitor
);
50 void OnDeviceAdded(const std::string
& device_path
) override
{
51 task_runner_
->PostTask(
52 FROM_HERE
, base::Bind(&UsbServiceImpl::RefreshDevices
, usb_service_
));
55 void OnDeviceRemoved(const std::string
& device_path
) override
{
56 task_runner_
->PostTask(
57 FROM_HERE
, base::Bind(&UsbServiceImpl::RefreshDevices
, usb_service_
));
60 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
61 base::WeakPtr
<UsbServiceImpl
> usb_service_
;
62 ScopedObserver
<DeviceMonitorWin
, DeviceMonitorWin::Observer
> device_observer_
;
67 UsbService
* UsbServiceImpl::Create(
68 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
) {
69 PlatformUsbContext context
= NULL
;
70 const int rv
= libusb_init(&context
);
71 if (rv
!= LIBUSB_SUCCESS
) {
72 VLOG(1) << "Failed to initialize libusb: "
73 << ConvertPlatformUsbErrorToString(rv
);
80 return new UsbServiceImpl(context
, ui_task_runner
);
83 scoped_refptr
<UsbDevice
> UsbServiceImpl::GetDeviceById(uint32 unique_id
) {
84 DCHECK(CalledOnValidThread());
86 DeviceMap::iterator it
= devices_
.find(unique_id
);
87 if (it
!= devices_
.end()) {
93 void UsbServiceImpl::GetDevices(
94 std::vector
<scoped_refptr
<UsbDevice
> >* devices
) {
95 DCHECK(CalledOnValidThread());
96 STLClearObject(devices
);
98 if (!hotplug_enabled_
) {
102 for (const auto& map_entry
: devices_
) {
103 devices
->push_back(map_entry
.second
);
107 UsbServiceImpl::UsbServiceImpl(
108 PlatformUsbContext context
,
109 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
)
110 : context_(new UsbContext(context
)),
111 ui_task_runner_(ui_task_runner
),
113 hotplug_enabled_(false),
114 weak_factory_(this) {
115 task_runner_
= base::ThreadTaskRunnerHandle::Get();
116 int rv
= libusb_hotplug_register_callback(
118 static_cast<libusb_hotplug_event
>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED
|
119 LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT
),
120 LIBUSB_HOTPLUG_ENUMERATE
, LIBUSB_HOTPLUG_MATCH_ANY
,
121 LIBUSB_HOTPLUG_MATCH_ANY
, LIBUSB_HOTPLUG_MATCH_ANY
,
122 &UsbServiceImpl::HotplugCallback
, this, &hotplug_handle_
);
123 if (rv
== LIBUSB_SUCCESS
) {
124 hotplug_enabled_
= true;
127 ui_thread_helper_
= new UIThreadHelper(weak_factory_
.GetWeakPtr());
128 ui_task_runner_
->PostTask(FROM_HERE
,
129 base::Bind(&UIThreadHelper::Start
,
130 base::Unretained(ui_thread_helper_
)));
135 UsbServiceImpl::~UsbServiceImpl() {
136 if (hotplug_enabled_
) {
137 libusb_hotplug_deregister_callback(context_
->context(), hotplug_handle_
);
140 if (ui_thread_helper_
) {
141 ui_task_runner_
->DeleteSoon(FROM_HERE
, ui_thread_helper_
);
144 for (const auto& map_entry
: devices_
) {
145 map_entry
.second
->OnDisconnect();
149 void UsbServiceImpl::RefreshDevices() {
150 DCHECK(CalledOnValidThread());
152 libusb_device
** platform_devices
= NULL
;
153 const ssize_t device_count
=
154 libusb_get_device_list(context_
->context(), &platform_devices
);
155 if (device_count
< 0) {
156 VLOG(1) << "Failed to get device list: "
157 << ConvertPlatformUsbErrorToString(device_count
);
160 std::set
<UsbDevice
*> connected_devices
;
161 std::vector
<PlatformUsbDevice
> disconnected_devices
;
163 // Populates new devices.
164 for (ssize_t i
= 0; i
< device_count
; ++i
) {
165 if (!ContainsKey(platform_devices_
, platform_devices
[i
])) {
166 scoped_refptr
<UsbDeviceImpl
> new_device
= AddDevice(platform_devices
[i
]);
168 connected_devices
.insert(new_device
.get());
171 connected_devices
.insert(platform_devices_
[platform_devices
[i
]].get());
175 // Find disconnected devices.
176 for (const auto& map_entry
: platform_devices_
) {
177 PlatformUsbDevice platform_device
= map_entry
.first
;
178 scoped_refptr
<UsbDeviceImpl
> device
= map_entry
.second
;
179 if (!ContainsKey(connected_devices
, device
.get())) {
180 disconnected_devices
.push_back(platform_device
);
181 devices_
.erase(device
->unique_id());
183 NotifyDeviceRemoved(device
);
184 device
->OnDisconnect();
188 // Remove disconnected devices from platform_devices_.
189 for (const PlatformUsbDevice
& platform_device
: disconnected_devices
) {
190 // UsbDevice will be destroyed after this. The corresponding
191 // PlatformUsbDevice will be unref'ed during this process.
192 platform_devices_
.erase(platform_device
);
195 libusb_free_device_list(platform_devices
, true);
198 scoped_refptr
<UsbDeviceImpl
> UsbServiceImpl::AddDevice(
199 PlatformUsbDevice platform_device
) {
200 libusb_device_descriptor descriptor
;
201 int rv
= libusb_get_device_descriptor(platform_device
, &descriptor
);
202 if (rv
== LIBUSB_SUCCESS
) {
205 unique_id
= ++next_unique_id_
;
206 } while (devices_
.find(unique_id
) != devices_
.end());
208 scoped_refptr
<UsbDeviceImpl
> new_device(new UsbDeviceImpl(
209 context_
, ui_task_runner_
, platform_device
, descriptor
.idVendor
,
210 descriptor
.idProduct
, unique_id
));
211 platform_devices_
[platform_device
] = new_device
;
212 devices_
[unique_id
] = new_device
;
213 NotifyDeviceAdded(new_device
);
216 VLOG(1) << "Failed to get device descriptor: "
217 << ConvertPlatformUsbErrorToString(rv
);
223 int LIBUSB_CALL
UsbServiceImpl::HotplugCallback(libusb_context
* context
,
224 PlatformUsbDevice device
,
225 libusb_hotplug_event event
,
227 // It is safe to access the UsbServiceImpl* here because libusb takes a lock
228 // around registering, deregistering and calling hotplug callback functions
229 // and so guarantees that this function will not be called by the event
230 // processing thread after it has been deregistered.
231 UsbServiceImpl
* self
= reinterpret_cast<UsbServiceImpl
*>(user_data
);
233 case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED
:
234 libusb_ref_device(device
); // Released in OnDeviceAdded.
235 if (self
->task_runner_
->BelongsToCurrentThread()) {
236 self
->OnDeviceAdded(device
);
238 self
->task_runner_
->PostTask(
239 FROM_HERE
, base::Bind(&UsbServiceImpl::OnDeviceAdded
,
240 base::Unretained(self
), device
));
243 case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT
:
244 libusb_ref_device(device
); // Released in OnDeviceRemoved.
245 if (self
->task_runner_
->BelongsToCurrentThread()) {
246 self
->OnDeviceRemoved(device
);
248 self
->task_runner_
->PostTask(
249 FROM_HERE
, base::Bind(&UsbServiceImpl::OnDeviceRemoved
,
250 base::Unretained(self
), device
));
260 void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device
) {
261 DCHECK(CalledOnValidThread());
262 DCHECK(!ContainsKey(platform_devices_
, platform_device
));
264 AddDevice(platform_device
);
265 libusb_unref_device(platform_device
);
268 void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device
) {
269 DCHECK(CalledOnValidThread());
271 PlatformDeviceMap::iterator it
= platform_devices_
.find(platform_device
);
272 if (it
!= platform_devices_
.end()) {
273 scoped_refptr
<UsbDeviceImpl
> device
= it
->second
;
274 DeviceMap::iterator dev_it
= devices_
.find(device
->unique_id());
275 if (dev_it
!= devices_
.end()) {
276 devices_
.erase(dev_it
);
280 platform_devices_
.erase(it
);
282 NotifyDeviceRemoved(device
);
283 device
->OnDisconnect();
288 libusb_unref_device(platform_device
);
291 } // namespace device