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 "components/device_event_log/device_event_log.h"
16 #include "device/usb/usb_error.h"
21 #include "base/scoped_observer.h"
22 #include "device/core/device_monitor_win.h"
28 // This class lives on the application main thread so that it can listen for
29 // device change notification window messages. It registers for notifications
30 // regarding devices implementating the "UsbDevice" interface, which represents
31 // most of the devices the UsbService will enumerate.
32 class UsbServiceImpl::UIThreadHelper final
33 : private DeviceMonitorWin::Observer
{
35 UIThreadHelper(base::WeakPtr
<UsbServiceImpl
> usb_service
)
36 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
37 usb_service_(usb_service
),
38 device_observer_(this) {}
43 DeviceMonitorWin
* device_monitor
=
44 DeviceMonitorWin::GetForDeviceInterface(GUID_DEVINTERFACE_USB_DEVICE
);
46 device_observer_
.Add(device_monitor
);
51 void OnDeviceAdded(const std::string
& device_path
) override
{
52 task_runner_
->PostTask(
53 FROM_HERE
, base::Bind(&UsbServiceImpl::RefreshDevices
, usb_service_
));
56 void OnDeviceRemoved(const std::string
& device_path
) override
{
57 task_runner_
->PostTask(
58 FROM_HERE
, base::Bind(&UsbServiceImpl::RefreshDevices
, usb_service_
));
61 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
62 base::WeakPtr
<UsbServiceImpl
> usb_service_
;
63 ScopedObserver
<DeviceMonitorWin
, DeviceMonitorWin::Observer
> device_observer_
;
68 UsbService
* UsbServiceImpl::Create(
69 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
) {
70 PlatformUsbContext context
= NULL
;
71 const int rv
= libusb_init(&context
);
72 if (rv
!= LIBUSB_SUCCESS
) {
73 USB_LOG(ERROR
) << "Failed to initialize libusb: "
74 << ConvertPlatformUsbErrorToString(rv
);
81 return new UsbServiceImpl(context
, ui_task_runner
);
84 scoped_refptr
<UsbDevice
> UsbServiceImpl::GetDeviceById(uint32 unique_id
) {
85 DCHECK(CalledOnValidThread());
87 DeviceMap::iterator it
= devices_
.find(unique_id
);
88 if (it
!= devices_
.end()) {
94 void UsbServiceImpl::GetDevices(
95 std::vector
<scoped_refptr
<UsbDevice
> >* devices
) {
96 DCHECK(CalledOnValidThread());
97 STLClearObject(devices
);
99 if (!hotplug_enabled_
) {
103 for (const auto& map_entry
: devices_
) {
104 devices
->push_back(map_entry
.second
);
108 UsbServiceImpl::UsbServiceImpl(
109 PlatformUsbContext context
,
110 scoped_refptr
<base::SingleThreadTaskRunner
> ui_task_runner
)
111 : context_(new UsbContext(context
)),
112 ui_task_runner_(ui_task_runner
),
114 hotplug_enabled_(false),
115 weak_factory_(this) {
116 task_runner_
= base::ThreadTaskRunnerHandle::Get();
117 int rv
= libusb_hotplug_register_callback(
119 static_cast<libusb_hotplug_event
>(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED
|
120 LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT
),
121 LIBUSB_HOTPLUG_ENUMERATE
, LIBUSB_HOTPLUG_MATCH_ANY
,
122 LIBUSB_HOTPLUG_MATCH_ANY
, LIBUSB_HOTPLUG_MATCH_ANY
,
123 &UsbServiceImpl::HotplugCallback
, this, &hotplug_handle_
);
124 if (rv
== LIBUSB_SUCCESS
) {
125 hotplug_enabled_
= true;
128 ui_thread_helper_
= new UIThreadHelper(weak_factory_
.GetWeakPtr());
129 ui_task_runner_
->PostTask(FROM_HERE
,
130 base::Bind(&UIThreadHelper::Start
,
131 base::Unretained(ui_thread_helper_
)));
136 UsbServiceImpl::~UsbServiceImpl() {
137 if (hotplug_enabled_
) {
138 libusb_hotplug_deregister_callback(context_
->context(), hotplug_handle_
);
141 if (ui_thread_helper_
) {
142 ui_task_runner_
->DeleteSoon(FROM_HERE
, ui_thread_helper_
);
145 for (const auto& map_entry
: devices_
) {
146 map_entry
.second
->OnDisconnect();
150 void UsbServiceImpl::RefreshDevices() {
151 DCHECK(CalledOnValidThread());
153 libusb_device
** platform_devices
= NULL
;
154 const ssize_t device_count
=
155 libusb_get_device_list(context_
->context(), &platform_devices
);
156 if (device_count
< 0) {
157 USB_LOG(ERROR
) << "Failed to get device list: "
158 << ConvertPlatformUsbErrorToString(device_count
);
161 std::set
<UsbDevice
*> connected_devices
;
162 std::vector
<PlatformUsbDevice
> disconnected_devices
;
164 // Populates new devices.
165 for (ssize_t i
= 0; i
< device_count
; ++i
) {
166 if (!ContainsKey(platform_devices_
, platform_devices
[i
])) {
167 scoped_refptr
<UsbDeviceImpl
> new_device
= AddDevice(platform_devices
[i
]);
169 connected_devices
.insert(new_device
.get());
172 connected_devices
.insert(platform_devices_
[platform_devices
[i
]].get());
176 // Find disconnected devices.
177 for (const auto& map_entry
: platform_devices_
) {
178 PlatformUsbDevice platform_device
= map_entry
.first
;
179 scoped_refptr
<UsbDeviceImpl
> device
= map_entry
.second
;
180 if (!ContainsKey(connected_devices
, device
.get())) {
181 disconnected_devices
.push_back(platform_device
);
182 devices_
.erase(device
->unique_id());
184 NotifyDeviceRemoved(device
);
185 device
->OnDisconnect();
189 // Remove disconnected devices from platform_devices_.
190 for (const PlatformUsbDevice
& platform_device
: disconnected_devices
) {
191 // UsbDevice will be destroyed after this. The corresponding
192 // PlatformUsbDevice will be unref'ed during this process.
193 platform_devices_
.erase(platform_device
);
196 libusb_free_device_list(platform_devices
, true);
199 scoped_refptr
<UsbDeviceImpl
> UsbServiceImpl::AddDevice(
200 PlatformUsbDevice platform_device
) {
201 libusb_device_descriptor descriptor
;
202 int rv
= libusb_get_device_descriptor(platform_device
, &descriptor
);
203 if (rv
== LIBUSB_SUCCESS
) {
206 unique_id
= ++next_unique_id_
;
207 } while (devices_
.find(unique_id
) != devices_
.end());
209 scoped_refptr
<UsbDeviceImpl
> new_device(new UsbDeviceImpl(
210 context_
, ui_task_runner_
, platform_device
, descriptor
.idVendor
,
211 descriptor
.idProduct
, unique_id
));
212 platform_devices_
[platform_device
] = new_device
;
213 devices_
[unique_id
] = new_device
;
214 NotifyDeviceAdded(new_device
);
217 USB_LOG(EVENT
) << "Failed to get device descriptor: "
218 << ConvertPlatformUsbErrorToString(rv
);
224 int LIBUSB_CALL
UsbServiceImpl::HotplugCallback(libusb_context
* context
,
225 PlatformUsbDevice device
,
226 libusb_hotplug_event event
,
228 // It is safe to access the UsbServiceImpl* here because libusb takes a lock
229 // around registering, deregistering and calling hotplug callback functions
230 // and so guarantees that this function will not be called by the event
231 // processing thread after it has been deregistered.
232 UsbServiceImpl
* self
= reinterpret_cast<UsbServiceImpl
*>(user_data
);
234 case LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED
:
235 libusb_ref_device(device
); // Released in OnDeviceAdded.
236 if (self
->task_runner_
->BelongsToCurrentThread()) {
237 self
->OnDeviceAdded(device
);
239 self
->task_runner_
->PostTask(
240 FROM_HERE
, base::Bind(&UsbServiceImpl::OnDeviceAdded
,
241 base::Unretained(self
), device
));
244 case LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT
:
245 libusb_ref_device(device
); // Released in OnDeviceRemoved.
246 if (self
->task_runner_
->BelongsToCurrentThread()) {
247 self
->OnDeviceRemoved(device
);
249 self
->task_runner_
->PostTask(
250 FROM_HERE
, base::Bind(&UsbServiceImpl::OnDeviceRemoved
,
251 base::Unretained(self
), device
));
261 void UsbServiceImpl::OnDeviceAdded(PlatformUsbDevice platform_device
) {
262 DCHECK(CalledOnValidThread());
263 DCHECK(!ContainsKey(platform_devices_
, platform_device
));
265 AddDevice(platform_device
);
266 libusb_unref_device(platform_device
);
269 void UsbServiceImpl::OnDeviceRemoved(PlatformUsbDevice platform_device
) {
270 DCHECK(CalledOnValidThread());
272 PlatformDeviceMap::iterator it
= platform_devices_
.find(platform_device
);
273 if (it
!= platform_devices_
.end()) {
274 scoped_refptr
<UsbDeviceImpl
> device
= it
->second
;
275 DeviceMap::iterator dev_it
= devices_
.find(device
->unique_id());
276 if (dev_it
!= devices_
.end()) {
277 devices_
.erase(dev_it
);
281 platform_devices_
.erase(it
);
283 NotifyDeviceRemoved(device
);
284 device
->OnDisconnect();
289 libusb_unref_device(platform_device
);
292 } // namespace device