1 // Copyright 2015 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/devices_app/usb/device_manager_impl.h"
8 #include "base/location.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/scoped_observer.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/stl_util.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "device/core/device_client.h"
16 #include "device/devices_app/usb/device_impl.h"
17 #include "device/devices_app/usb/public/interfaces/device.mojom.h"
18 #include "device/devices_app/usb/type_converters.h"
19 #include "device/usb/usb_device.h"
20 #include "device/usb/usb_device_filter.h"
21 #include "device/usb/usb_service.h"
22 #include "third_party/mojo/src/mojo/public/cpp/bindings/array.h"
23 #include "third_party/mojo/src/mojo/public/cpp/bindings/interface_request.h"
30 using DeviceList
= DeviceManagerImpl::DeviceList
;
31 using DeviceMap
= DeviceManagerImpl::DeviceMap
;
33 void OnGetDevicesOnServiceThread(
34 const base::Callback
<void(const DeviceList
&)>& callback
,
35 scoped_refptr
<base::TaskRunner
> callback_task_runner
,
36 const DeviceList
& devices
) {
37 callback_task_runner
->PostTask(FROM_HERE
, base::Bind(callback
, devices
));
40 void GetDevicesOnServiceThread(
41 const base::Callback
<void(const DeviceList
&)>& callback
,
42 scoped_refptr
<base::TaskRunner
> callback_task_runner
) {
43 DCHECK(DeviceClient::Get());
44 UsbService
* usb_service
= DeviceClient::Get()->GetUsbService();
46 usb_service
->GetDevices(base::Bind(&OnGetDevicesOnServiceThread
, callback
,
47 callback_task_runner
));
49 callback_task_runner
->PostTask(FROM_HERE
,
50 base::Bind(callback
, DeviceList()));
54 void GetDeviceOnServiceThread(
55 const mojo::String
& guid
,
56 const base::Callback
<void(scoped_refptr
<UsbDevice
>)>& callback
,
57 scoped_refptr
<base::TaskRunner
> callback_task_runner
) {
58 DCHECK(DeviceClient::Get());
59 scoped_refptr
<UsbDevice
> device
;
60 UsbService
* usb_service
= DeviceClient::Get()->GetUsbService();
62 device
= usb_service
->GetDevice(guid
);
63 callback_task_runner
->PostTask(FROM_HERE
, base::Bind(callback
, device
));
66 void FilterAndConvertDevicesAndThen(
67 const DeviceMap
& devices
,
68 const DeviceManagerImpl::GetDevicesCallback
& callback
,
69 mojo::Array
<mojo::String
> allowed_guids
) {
70 mojo::Array
<DeviceInfoPtr
> allowed_devices(allowed_guids
.size());
71 for (size_t i
= 0; i
< allowed_guids
.size(); ++i
) {
72 const auto it
= devices
.find(allowed_guids
[i
]);
73 DCHECK(it
!= devices
.end());
74 allowed_devices
[i
] = DeviceInfo::From(*it
->second
);
77 callback
.Run(allowed_devices
.Pass());
82 class DeviceManagerImpl::ServiceThreadHelper
83 : public UsbService::Observer
,
84 public base::MessageLoop::DestructionObserver
{
86 ServiceThreadHelper(base::WeakPtr
<DeviceManagerImpl
> manager
,
87 scoped_refptr
<base::TaskRunner
> task_runner
)
88 : observer_(this), manager_(manager
), task_runner_(task_runner
) {}
90 ~ServiceThreadHelper() override
{
91 base::MessageLoop::current()->RemoveDestructionObserver(this);
94 static void Start(scoped_ptr
<ServiceThreadHelper
> self
) {
95 UsbService
* usb_service
= DeviceClient::Get()->GetUsbService();
97 self
->observer_
.Add(usb_service
);
99 // |self| now owned by the current message loop.
100 base::MessageLoop::current()->AddDestructionObserver(self
.release());
104 // UsbService::Observer
105 void OnDeviceAdded(scoped_refptr
<UsbDevice
> device
) override
{
106 task_runner_
->PostTask(
108 base::Bind(&DeviceManagerImpl::OnDeviceAdded
, manager_
, device
));
111 void OnDeviceRemoved(scoped_refptr
<UsbDevice
> device
) override
{
112 task_runner_
->PostTask(
114 base::Bind(&DeviceManagerImpl::OnDeviceRemoved
, manager_
, device
));
117 void WillDestroyUsbService() override
{ observer_
.RemoveAll(); }
119 // base::MessageLoop::DestructionObserver
120 void WillDestroyCurrentMessageLoop() override
{ delete this; }
122 ScopedObserver
<UsbService
, UsbService::Observer
> observer_
;
123 base::WeakPtr
<DeviceManagerImpl
> manager_
;
124 scoped_refptr
<base::TaskRunner
> task_runner_
;
127 DeviceManagerImpl::DeviceManagerImpl(
128 mojo::InterfaceRequest
<DeviceManager
> request
,
129 PermissionProviderPtr permission_provider
,
130 scoped_refptr
<base::SequencedTaskRunner
> service_task_runner
)
131 : permission_provider_(permission_provider
.Pass()),
132 service_task_runner_(service_task_runner
),
133 binding_(this, request
.Pass()),
134 weak_factory_(this) {
135 // This object owns itself and will be destroyed if either the message pipe
136 // it is bound to is closed or the PermissionProvider it depends on is
138 binding_
.set_connection_error_handler([this]() { delete this; });
139 permission_provider_
.set_connection_error_handler([this]() { delete this; });
141 scoped_ptr
<ServiceThreadHelper
> helper(new ServiceThreadHelper(
142 weak_factory_
.GetWeakPtr(), base::ThreadTaskRunnerHandle::Get()));
143 helper_
= helper
.get();
144 service_task_runner_
->PostTask(
146 base::Bind(&ServiceThreadHelper::Start
, base::Passed(&helper
)));
149 DeviceManagerImpl::~DeviceManagerImpl() {
150 // It is safe to call this if |helper_| was already destroyed when
151 // |service_task_runner_| exited as the task will never execute.
152 service_task_runner_
->DeleteSoon(FROM_HERE
, helper_
);
153 connection_error_handler_
.Run();
156 void DeviceManagerImpl::GetDevices(EnumerationOptionsPtr options
,
157 const GetDevicesCallback
& callback
) {
158 auto get_devices_callback
=
159 base::Bind(&DeviceManagerImpl::OnGetDevices
, weak_factory_
.GetWeakPtr(),
160 base::Passed(&options
), callback
);
161 service_task_runner_
->PostTask(
162 FROM_HERE
, base::Bind(&GetDevicesOnServiceThread
, get_devices_callback
,
163 base::ThreadTaskRunnerHandle::Get()));
166 void DeviceManagerImpl::GetDeviceChanges(
167 const GetDeviceChangesCallback
& callback
) {
168 device_change_callbacks_
.push(callback
);
169 MaybeRunDeviceChangesCallback();
172 void DeviceManagerImpl::GetDevice(
173 const mojo::String
& guid
,
174 mojo::InterfaceRequest
<Device
> device_request
) {
175 auto get_device_callback
=
176 base::Bind(&DeviceManagerImpl::OnGetDevice
, weak_factory_
.GetWeakPtr(),
177 base::Passed(&device_request
));
178 service_task_runner_
->PostTask(
180 base::Bind(&GetDeviceOnServiceThread
, guid
, get_device_callback
,
181 base::ThreadTaskRunnerHandle::Get()));
184 void DeviceManagerImpl::OnGetDevice(
185 mojo::InterfaceRequest
<Device
> device_request
,
186 scoped_refptr
<UsbDevice
> device
) {
190 mojo::Array
<DeviceInfoPtr
> requested_devices(1);
191 requested_devices
[0] = DeviceInfo::From(*device
);
192 permission_provider_
->HasDevicePermission(
193 requested_devices
.Pass(),
194 base::Bind(&DeviceManagerImpl::OnGetDevicePermissionCheckComplete
,
195 base::Unretained(this), device
,
196 base::Passed(&device_request
)));
199 void DeviceManagerImpl::OnGetDevicePermissionCheckComplete(
200 scoped_refptr
<UsbDevice
> device
,
201 mojo::InterfaceRequest
<Device
> device_request
,
202 mojo::Array
<mojo::String
> allowed_guids
) {
203 if (allowed_guids
.size() == 0)
206 DCHECK(allowed_guids
.size() == 1);
207 new DeviceImpl(device
, device_request
.Pass());
210 void DeviceManagerImpl::OnGetDevices(EnumerationOptionsPtr options
,
211 const GetDevicesCallback
& callback
,
212 const DeviceList
& devices
) {
213 std::vector
<UsbDeviceFilter
> filters
;
215 filters
= options
->filters
.To
<std::vector
<UsbDeviceFilter
>>();
217 std::map
<std::string
, scoped_refptr
<UsbDevice
>> device_map
;
218 mojo::Array
<DeviceInfoPtr
> requested_devices(0);
219 for (const auto& device
: devices
) {
220 if (filters
.empty() || UsbDeviceFilter::MatchesAny(device
, filters
)) {
221 device_map
[device
->guid()] = device
;
222 requested_devices
.push_back(DeviceInfo::From(*device
));
226 permission_provider_
->HasDevicePermission(
227 requested_devices
.Pass(),
228 base::Bind(&FilterAndConvertDevicesAndThen
, device_map
, callback
));
231 void DeviceManagerImpl::OnDeviceAdded(scoped_refptr
<UsbDevice
> device
) {
232 DCHECK(!ContainsKey(devices_removed_
, device
->guid()));
233 devices_added_
[device
->guid()] = device
;
234 MaybeRunDeviceChangesCallback();
237 void DeviceManagerImpl::OnDeviceRemoved(scoped_refptr
<UsbDevice
> device
) {
238 if (devices_added_
.erase(device
->guid()) == 0)
239 devices_removed_
[device
->guid()] = device
;
240 MaybeRunDeviceChangesCallback();
243 void DeviceManagerImpl::MaybeRunDeviceChangesCallback() {
244 if (!permission_request_pending_
&& !device_change_callbacks_
.empty()) {
245 DeviceMap devices_added
;
246 devices_added
.swap(devices_added_
);
247 DeviceMap devices_removed
;
248 devices_removed
.swap(devices_removed_
);
250 mojo::Array
<DeviceInfoPtr
> requested_devices(devices_added
.size() +
251 devices_removed
.size());
254 for (const auto& map_entry
: devices_added
)
255 requested_devices
[i
++] = DeviceInfo::From(*map_entry
.second
);
256 for (const auto& map_entry
: devices_removed
)
257 requested_devices
[i
++] = DeviceInfo::From(*map_entry
.second
);
260 permission_request_pending_
= true;
261 permission_provider_
->HasDevicePermission(
262 requested_devices
.Pass(),
263 base::Bind(&DeviceManagerImpl::OnEnumerationPermissionCheckComplete
,
264 base::Unretained(this), devices_added
, devices_removed
));
268 void DeviceManagerImpl::OnEnumerationPermissionCheckComplete(
269 const DeviceMap
& devices_added
,
270 const DeviceMap
& devices_removed
,
271 mojo::Array
<mojo::String
> allowed_guids
) {
272 permission_request_pending_
= false;
274 if (allowed_guids
.size() > 0) {
275 DeviceChangeNotificationPtr notification
= DeviceChangeNotification::New();
276 notification
->devices_added
.resize(0);
277 notification
->devices_removed
.resize(0);
279 for (size_t i
= 0; i
< allowed_guids
.size(); ++i
) {
280 const mojo::String
& guid
= allowed_guids
[i
];
281 auto it
= devices_added
.find(guid
);
282 if (it
!= devices_added
.end()) {
283 DCHECK(!ContainsKey(devices_removed
, guid
));
284 notification
->devices_added
.push_back(DeviceInfo::From(*it
->second
));
286 it
= devices_removed
.find(guid
);
287 DCHECK(it
!= devices_removed
.end());
288 notification
->devices_removed
.push_back(DeviceInfo::From(*it
->second
));
292 DCHECK(!device_change_callbacks_
.empty());
293 const GetDeviceChangesCallback
& callback
= device_change_callbacks_
.front();
294 callback
.Run(notification
.Pass());
295 device_change_callbacks_
.pop();
298 if (devices_added_
.size() > 0 || !devices_removed_
.empty())
299 MaybeRunDeviceChangesCallback();
303 } // namespace device