Make Chromoting browser tests pass again
[chromium-blink-merge.git] / device / usb / usb_service_impl.cc
blobd63c493dd632f59533966550ff1ae3441f1f1c14
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"
7 #include <set>
9 #include "base/bind.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"
18 #if defined(OS_WIN)
19 #include <usbiodef.h>
21 #include "base/scoped_observer.h"
22 #include "device/core/device_monitor_win.h"
23 #endif // OS_WIN
25 namespace device {
27 #if defined(OS_WIN)
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 {
34 public:
35 UIThreadHelper(base::WeakPtr<UsbServiceImpl> usb_service)
36 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
37 usb_service_(usb_service),
38 device_observer_(this) {}
40 ~UIThreadHelper() {}
42 void Start() {
43 DeviceMonitorWin* device_monitor =
44 DeviceMonitorWin::GetForDeviceInterface(GUID_DEVINTERFACE_USB_DEVICE);
45 if (device_monitor) {
46 device_observer_.Add(device_monitor);
50 private:
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_;
65 #endif
67 // static
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);
75 return nullptr;
77 if (!context) {
78 return nullptr;
81 return new UsbServiceImpl(context, ui_task_runner);
84 scoped_refptr<UsbDevice> UsbServiceImpl::GetDeviceById(uint32 unique_id) {
85 DCHECK(CalledOnValidThread());
86 RefreshDevices();
87 DeviceMap::iterator it = devices_.find(unique_id);
88 if (it != devices_.end()) {
89 return it->second;
91 return NULL;
94 void UsbServiceImpl::GetDevices(
95 std::vector<scoped_refptr<UsbDevice> >* devices) {
96 DCHECK(CalledOnValidThread());
97 STLClearObject(devices);
99 if (!hotplug_enabled_) {
100 RefreshDevices();
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),
113 next_unique_id_(0),
114 hotplug_enabled_(false),
115 weak_factory_(this) {
116 task_runner_ = base::ThreadTaskRunnerHandle::Get();
117 int rv = libusb_hotplug_register_callback(
118 context_->context(),
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;
126 } else {
127 #if defined(OS_WIN)
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_)));
132 #endif // OS_WIN
136 UsbServiceImpl::~UsbServiceImpl() {
137 if (hotplug_enabled_) {
138 libusb_hotplug_deregister_callback(context_->context(), hotplug_handle_);
140 #if defined(OS_WIN)
141 if (ui_thread_helper_) {
142 ui_task_runner_->DeleteSoon(FROM_HERE, ui_thread_helper_);
144 #endif // OS_WIN
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]);
168 if (new_device) {
169 connected_devices.insert(new_device.get());
171 } else {
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) {
204 uint32 unique_id;
205 do {
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);
215 return new_device;
216 } else {
217 USB_LOG(EVENT) << "Failed to get device descriptor: "
218 << ConvertPlatformUsbErrorToString(rv);
219 return nullptr;
223 // static
224 int LIBUSB_CALL UsbServiceImpl::HotplugCallback(libusb_context* context,
225 PlatformUsbDevice device,
226 libusb_hotplug_event event,
227 void* user_data) {
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);
233 switch (event) {
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);
238 } else {
239 self->task_runner_->PostTask(
240 FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceAdded,
241 base::Unretained(self), device));
243 break;
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);
248 } else {
249 self->task_runner_->PostTask(
250 FROM_HERE, base::Bind(&UsbServiceImpl::OnDeviceRemoved,
251 base::Unretained(self), device));
253 break;
254 default:
255 NOTREACHED();
258 return 0;
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);
278 } else {
279 NOTREACHED();
281 platform_devices_.erase(it);
283 NotifyDeviceRemoved(device);
284 device->OnDisconnect();
285 } else {
286 NOTREACHED();
289 libusb_unref_device(platform_device);
292 } // namespace device