OLD | NEW |
| (Empty) |
1 // Copyright 2013 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. | |
4 | |
5 #include "device/bluetooth/bluetooth_socket_bluez.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include <memory> | |
10 #include <queue> | |
11 #include <string> | |
12 #include <utility> | |
13 | |
14 #include "base/bind.h" | |
15 #include "base/callback.h" | |
16 #include "base/logging.h" | |
17 #include "base/memory/linked_ptr.h" | |
18 #include "base/memory/ref_counted.h" | |
19 #include "base/message_loop/message_loop.h" | |
20 #include "base/sequenced_task_runner.h" | |
21 #include "base/strings/string_util.h" | |
22 #include "base/task_runner_util.h" | |
23 #include "base/threading/thread_restrictions.h" | |
24 #include "base/threading/worker_pool.h" | |
25 #include "dbus/bus.h" | |
26 #include "dbus/file_descriptor.h" | |
27 #include "dbus/object_path.h" | |
28 #include "device/bluetooth/bluetooth_adapter.h" | |
29 #include "device/bluetooth/bluetooth_adapter_bluez.h" | |
30 #include "device/bluetooth/bluetooth_adapter_profile_bluez.h" | |
31 #include "device/bluetooth/bluetooth_device.h" | |
32 #include "device/bluetooth/bluetooth_device_bluez.h" | |
33 #include "device/bluetooth/bluetooth_socket.h" | |
34 #include "device/bluetooth/bluetooth_socket_net.h" | |
35 #include "device/bluetooth/bluetooth_socket_thread.h" | |
36 #include "device/bluetooth/dbus/bluetooth_device_client.h" | |
37 #include "device/bluetooth/dbus/bluetooth_profile_manager_client.h" | |
38 #include "device/bluetooth/dbus/bluetooth_profile_service_provider.h" | |
39 #include "device/bluetooth/dbus/bluez_dbus_manager.h" | |
40 #include "net/base/ip_endpoint.h" | |
41 #include "net/base/net_errors.h" | |
42 #include "third_party/cros_system_api/dbus/service_constants.h" | |
43 | |
44 using device::BluetoothAdapter; | |
45 using device::BluetoothDevice; | |
46 using device::BluetoothSocketThread; | |
47 using device::BluetoothUUID; | |
48 | |
49 namespace { | |
50 | |
51 const char kAcceptFailed[] = "Failed to accept connection."; | |
52 const char kInvalidUUID[] = "Invalid UUID"; | |
53 const char kSocketNotListening[] = "Socket is not listening."; | |
54 | |
55 } // namespace | |
56 | |
57 namespace bluez { | |
58 | |
59 // static | |
60 scoped_refptr<BluetoothSocketBlueZ> BluetoothSocketBlueZ::CreateBluetoothSocket( | |
61 scoped_refptr<base::SequencedTaskRunner> ui_task_runner, | |
62 scoped_refptr<BluetoothSocketThread> socket_thread) { | |
63 DCHECK(ui_task_runner->RunsTasksOnCurrentThread()); | |
64 | |
65 return make_scoped_refptr( | |
66 new BluetoothSocketBlueZ(ui_task_runner, socket_thread)); | |
67 } | |
68 | |
69 BluetoothSocketBlueZ::AcceptRequest::AcceptRequest() {} | |
70 | |
71 BluetoothSocketBlueZ::AcceptRequest::~AcceptRequest() {} | |
72 | |
73 BluetoothSocketBlueZ::ConnectionRequest::ConnectionRequest() | |
74 : accepting(false), cancelled(false) {} | |
75 | |
76 BluetoothSocketBlueZ::ConnectionRequest::~ConnectionRequest() {} | |
77 | |
78 BluetoothSocketBlueZ::BluetoothSocketBlueZ( | |
79 scoped_refptr<base::SequencedTaskRunner> ui_task_runner, | |
80 scoped_refptr<BluetoothSocketThread> socket_thread) | |
81 : BluetoothSocketNet(ui_task_runner, socket_thread), profile_(nullptr) {} | |
82 | |
83 BluetoothSocketBlueZ::~BluetoothSocketBlueZ() { | |
84 DCHECK(!profile_); | |
85 | |
86 if (adapter_.get()) { | |
87 adapter_->RemoveObserver(this); | |
88 adapter_ = nullptr; | |
89 } | |
90 } | |
91 | |
92 void BluetoothSocketBlueZ::Connect( | |
93 const BluetoothDeviceBlueZ* device, | |
94 const BluetoothUUID& uuid, | |
95 SecurityLevel security_level, | |
96 const base::Closure& success_callback, | |
97 const ErrorCompletionCallback& error_callback) { | |
98 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
99 DCHECK(!profile_); | |
100 | |
101 if (!uuid.IsValid()) { | |
102 error_callback.Run(kInvalidUUID); | |
103 return; | |
104 } | |
105 | |
106 device_address_ = device->GetAddress(); | |
107 device_path_ = device->object_path(); | |
108 uuid_ = uuid; | |
109 options_.reset(new bluez::BluetoothProfileManagerClient::Options()); | |
110 if (security_level == SECURITY_LEVEL_LOW) | |
111 options_->require_authentication.reset(new bool(false)); | |
112 | |
113 adapter_ = device->adapter(); | |
114 | |
115 RegisterProfile(device->adapter(), success_callback, error_callback); | |
116 } | |
117 | |
118 void BluetoothSocketBlueZ::Listen( | |
119 scoped_refptr<BluetoothAdapter> adapter, | |
120 SocketType socket_type, | |
121 const BluetoothUUID& uuid, | |
122 const BluetoothAdapter::ServiceOptions& service_options, | |
123 const base::Closure& success_callback, | |
124 const ErrorCompletionCallback& error_callback) { | |
125 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
126 DCHECK(!profile_); | |
127 | |
128 if (!uuid.IsValid()) { | |
129 error_callback.Run(kInvalidUUID); | |
130 return; | |
131 } | |
132 | |
133 adapter_ = adapter; | |
134 adapter_->AddObserver(this); | |
135 | |
136 uuid_ = uuid; | |
137 options_.reset(new bluez::BluetoothProfileManagerClient::Options()); | |
138 if (service_options.name) | |
139 options_->name.reset(new std::string(*service_options.name)); | |
140 | |
141 switch (socket_type) { | |
142 case kRfcomm: | |
143 options_->channel.reset( | |
144 new uint16_t(service_options.channel ? *service_options.channel : 0)); | |
145 break; | |
146 case kL2cap: | |
147 options_->psm.reset( | |
148 new uint16_t(service_options.psm ? *service_options.psm : 0)); | |
149 break; | |
150 default: | |
151 NOTREACHED(); | |
152 } | |
153 | |
154 RegisterProfile(static_cast<BluetoothAdapterBlueZ*>(adapter.get()), | |
155 success_callback, error_callback); | |
156 } | |
157 | |
158 void BluetoothSocketBlueZ::Close() { | |
159 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
160 | |
161 if (profile_) | |
162 UnregisterProfile(); | |
163 | |
164 // In the case below, where an asynchronous task gets posted on the socket | |
165 // thread in BluetoothSocketNet::Close, a reference will be held to this | |
166 // socket by the callback. This may cause the BluetoothAdapter to outlive | |
167 // BluezDBusManager during shutdown if that callback executes too late. | |
168 if (adapter_.get()) { | |
169 adapter_->RemoveObserver(this); | |
170 adapter_ = nullptr; | |
171 } | |
172 | |
173 if (!device_path_.value().empty()) { | |
174 BluetoothSocketNet::Close(); | |
175 } else { | |
176 DoCloseListening(); | |
177 } | |
178 } | |
179 | |
180 void BluetoothSocketBlueZ::Disconnect(const base::Closure& callback) { | |
181 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
182 | |
183 if (profile_) | |
184 UnregisterProfile(); | |
185 | |
186 if (!device_path_.value().empty()) { | |
187 BluetoothSocketNet::Disconnect(callback); | |
188 } else { | |
189 DoCloseListening(); | |
190 callback.Run(); | |
191 } | |
192 } | |
193 | |
194 void BluetoothSocketBlueZ::Accept( | |
195 const AcceptCompletionCallback& success_callback, | |
196 const ErrorCompletionCallback& error_callback) { | |
197 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
198 | |
199 if (!device_path_.value().empty()) { | |
200 error_callback.Run(kSocketNotListening); | |
201 return; | |
202 } | |
203 | |
204 // Only one pending accept at a time | |
205 if (accept_request_.get()) { | |
206 error_callback.Run(net::ErrorToString(net::ERR_IO_PENDING)); | |
207 return; | |
208 } | |
209 | |
210 accept_request_.reset(new AcceptRequest); | |
211 accept_request_->success_callback = success_callback; | |
212 accept_request_->error_callback = error_callback; | |
213 | |
214 if (connection_request_queue_.size() >= 1) { | |
215 AcceptConnectionRequest(); | |
216 } | |
217 } | |
218 | |
219 void BluetoothSocketBlueZ::RegisterProfile( | |
220 BluetoothAdapterBlueZ* adapter, | |
221 const base::Closure& success_callback, | |
222 const ErrorCompletionCallback& error_callback) { | |
223 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
224 DCHECK(!profile_); | |
225 DCHECK(adapter); | |
226 | |
227 // If the adapter is not present, this is a listening socket and the | |
228 // adapter isn't running yet. Report success and carry on; | |
229 // the profile will be registered when the daemon becomes available. | |
230 if (!adapter->IsPresent()) { | |
231 VLOG(1) << uuid_.canonical_value() << " on " << device_path_.value() | |
232 << ": Delaying profile registration."; | |
233 base::MessageLoop::current()->PostTask(FROM_HERE, success_callback); | |
234 return; | |
235 } | |
236 | |
237 VLOG(1) << uuid_.canonical_value() << " on " << device_path_.value() | |
238 << ": Acquiring profile."; | |
239 | |
240 adapter->UseProfile(uuid_, device_path_, *options_, this, | |
241 base::Bind(&BluetoothSocketBlueZ::OnRegisterProfile, this, | |
242 success_callback, error_callback), | |
243 base::Bind(&BluetoothSocketBlueZ::OnRegisterProfileError, | |
244 this, error_callback)); | |
245 } | |
246 | |
247 void BluetoothSocketBlueZ::OnRegisterProfile( | |
248 const base::Closure& success_callback, | |
249 const ErrorCompletionCallback& error_callback, | |
250 BluetoothAdapterProfileBlueZ* profile) { | |
251 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
252 DCHECK(!profile_); | |
253 | |
254 profile_ = profile; | |
255 | |
256 if (device_path_.value().empty()) { | |
257 VLOG(1) << uuid_.canonical_value() << ": Profile registered."; | |
258 success_callback.Run(); | |
259 return; | |
260 } | |
261 | |
262 VLOG(1) << uuid_.canonical_value() << ": Got profile, connecting to " | |
263 << device_path_.value(); | |
264 | |
265 bluez::BluezDBusManager::Get()->GetBluetoothDeviceClient()->ConnectProfile( | |
266 device_path_, uuid_.canonical_value(), | |
267 base::Bind(&BluetoothSocketBlueZ::OnConnectProfile, this, | |
268 success_callback), | |
269 base::Bind(&BluetoothSocketBlueZ::OnConnectProfileError, this, | |
270 error_callback)); | |
271 } | |
272 | |
273 void BluetoothSocketBlueZ::OnRegisterProfileError( | |
274 const ErrorCompletionCallback& error_callback, | |
275 const std::string& error_message) { | |
276 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
277 | |
278 LOG(WARNING) << uuid_.canonical_value() | |
279 << ": Failed to register profile: " << error_message; | |
280 error_callback.Run(error_message); | |
281 } | |
282 | |
283 void BluetoothSocketBlueZ::OnConnectProfile( | |
284 const base::Closure& success_callback) { | |
285 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
286 DCHECK(profile_); | |
287 | |
288 VLOG(1) << profile_->object_path().value() << ": Profile connected."; | |
289 UnregisterProfile(); | |
290 success_callback.Run(); | |
291 } | |
292 | |
293 void BluetoothSocketBlueZ::OnConnectProfileError( | |
294 const ErrorCompletionCallback& error_callback, | |
295 const std::string& error_name, | |
296 const std::string& error_message) { | |
297 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
298 DCHECK(profile_); | |
299 | |
300 LOG(WARNING) << profile_->object_path().value() | |
301 << ": Failed to connect profile: " << error_name << ": " | |
302 << error_message; | |
303 UnregisterProfile(); | |
304 error_callback.Run(error_message); | |
305 } | |
306 | |
307 void BluetoothSocketBlueZ::AdapterPresentChanged(BluetoothAdapter* adapter, | |
308 bool present) { | |
309 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
310 | |
311 if (!present) { | |
312 // Adapter removed, we can't use the profile anymore. | |
313 UnregisterProfile(); | |
314 return; | |
315 } | |
316 | |
317 DCHECK(!profile_); | |
318 | |
319 VLOG(1) << uuid_.canonical_value() << " on " << device_path_.value() | |
320 << ": Acquiring profile."; | |
321 | |
322 static_cast<BluetoothAdapterBlueZ*>(adapter)->UseProfile( | |
323 uuid_, device_path_, *options_, this, | |
324 base::Bind(&BluetoothSocketBlueZ::OnInternalRegisterProfile, this), | |
325 base::Bind(&BluetoothSocketBlueZ::OnInternalRegisterProfileError, this)); | |
326 } | |
327 | |
328 void BluetoothSocketBlueZ::OnInternalRegisterProfile( | |
329 BluetoothAdapterProfileBlueZ* profile) { | |
330 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
331 DCHECK(!profile_); | |
332 | |
333 profile_ = profile; | |
334 | |
335 VLOG(1) << uuid_.canonical_value() << ": Profile re-registered"; | |
336 } | |
337 | |
338 void BluetoothSocketBlueZ::OnInternalRegisterProfileError( | |
339 const std::string& error_message) { | |
340 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
341 | |
342 LOG(WARNING) << "Failed to re-register profile: " << error_message; | |
343 } | |
344 | |
345 void BluetoothSocketBlueZ::Released() { | |
346 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
347 DCHECK(profile_); | |
348 | |
349 VLOG(1) << profile_->object_path().value() << ": Release"; | |
350 } | |
351 | |
352 void BluetoothSocketBlueZ::NewConnection( | |
353 const dbus::ObjectPath& device_path, | |
354 std::unique_ptr<dbus::FileDescriptor> fd, | |
355 const bluez::BluetoothProfileServiceProvider::Delegate::Options& options, | |
356 const ConfirmationCallback& callback) { | |
357 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
358 | |
359 VLOG(1) << uuid_.canonical_value() | |
360 << ": New connection from device: " << device_path.value(); | |
361 | |
362 if (!device_path_.value().empty()) { | |
363 DCHECK(device_path_ == device_path); | |
364 | |
365 socket_thread()->task_runner()->PostTask( | |
366 FROM_HERE, | |
367 base::Bind(&BluetoothSocketBlueZ::DoNewConnection, this, device_path_, | |
368 base::Passed(&fd), options, callback)); | |
369 } else { | |
370 linked_ptr<ConnectionRequest> request(new ConnectionRequest()); | |
371 request->device_path = device_path; | |
372 request->fd = std::move(fd); | |
373 request->options = options; | |
374 request->callback = callback; | |
375 | |
376 connection_request_queue_.push(request); | |
377 VLOG(1) << uuid_.canonical_value() << ": Connection is now pending."; | |
378 if (accept_request_) { | |
379 AcceptConnectionRequest(); | |
380 } | |
381 } | |
382 } | |
383 | |
384 void BluetoothSocketBlueZ::RequestDisconnection( | |
385 const dbus::ObjectPath& device_path, | |
386 const ConfirmationCallback& callback) { | |
387 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
388 DCHECK(profile_); | |
389 | |
390 VLOG(1) << profile_->object_path().value() << ": Request disconnection"; | |
391 callback.Run(SUCCESS); | |
392 } | |
393 | |
394 void BluetoothSocketBlueZ::Cancel() { | |
395 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
396 DCHECK(profile_); | |
397 | |
398 VLOG(1) << profile_->object_path().value() << ": Cancel"; | |
399 | |
400 if (!connection_request_queue_.size()) | |
401 return; | |
402 | |
403 // If the front request is being accepted mark it as cancelled, otherwise | |
404 // just pop it from the queue. | |
405 linked_ptr<ConnectionRequest> request = connection_request_queue_.front(); | |
406 if (!request->accepting) { | |
407 request->cancelled = true; | |
408 } else { | |
409 connection_request_queue_.pop(); | |
410 } | |
411 } | |
412 | |
413 void BluetoothSocketBlueZ::AcceptConnectionRequest() { | |
414 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
415 DCHECK(accept_request_.get()); | |
416 DCHECK(connection_request_queue_.size() >= 1); | |
417 DCHECK(profile_); | |
418 | |
419 VLOG(1) << profile_->object_path().value() | |
420 << ": Accepting pending connection."; | |
421 | |
422 linked_ptr<ConnectionRequest> request = connection_request_queue_.front(); | |
423 request->accepting = true; | |
424 | |
425 BluetoothDeviceBlueZ* device = | |
426 static_cast<BluetoothAdapterBlueZ*>(adapter_.get()) | |
427 ->GetDeviceWithPath(request->device_path); | |
428 DCHECK(device); | |
429 | |
430 scoped_refptr<BluetoothSocketBlueZ> client_socket = | |
431 BluetoothSocketBlueZ::CreateBluetoothSocket(ui_task_runner(), | |
432 socket_thread()); | |
433 | |
434 client_socket->device_address_ = device->GetAddress(); | |
435 client_socket->device_path_ = request->device_path; | |
436 client_socket->uuid_ = uuid_; | |
437 | |
438 socket_thread()->task_runner()->PostTask( | |
439 FROM_HERE, | |
440 base::Bind(&BluetoothSocketBlueZ::DoNewConnection, client_socket, | |
441 request->device_path, base::Passed(&request->fd), | |
442 request->options, | |
443 base::Bind(&BluetoothSocketBlueZ::OnNewConnection, this, | |
444 client_socket, request->callback))); | |
445 } | |
446 | |
447 void BluetoothSocketBlueZ::DoNewConnection( | |
448 const dbus::ObjectPath& device_path, | |
449 std::unique_ptr<dbus::FileDescriptor> fd, | |
450 const bluez::BluetoothProfileServiceProvider::Delegate::Options& options, | |
451 const ConfirmationCallback& callback) { | |
452 DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread()); | |
453 base::ThreadRestrictions::AssertIOAllowed(); | |
454 fd->CheckValidity(); | |
455 | |
456 VLOG(1) << uuid_.canonical_value() << ": Validity check complete."; | |
457 if (!fd->is_valid()) { | |
458 LOG(WARNING) << uuid_.canonical_value() << " :" << fd->value() | |
459 << ": Invalid file descriptor received from Bluetooth Daemon."; | |
460 ui_task_runner()->PostTask(FROM_HERE, base::Bind(callback, REJECTED)); | |
461 return; | |
462 } | |
463 | |
464 if (tcp_socket()) { | |
465 LOG(WARNING) << uuid_.canonical_value() << ": Already connected"; | |
466 ui_task_runner()->PostTask(FROM_HERE, base::Bind(callback, REJECTED)); | |
467 return; | |
468 } | |
469 | |
470 ResetTCPSocket(); | |
471 | |
472 // Note: We don't have a meaningful |IPEndPoint|, but that is ok since the | |
473 // TCPSocket implementation does not actually require one. | |
474 int net_result = | |
475 tcp_socket()->AdoptConnectedSocket(fd->value(), net::IPEndPoint()); | |
476 if (net_result != net::OK) { | |
477 LOG(WARNING) << uuid_.canonical_value() << ": Error adopting socket: " | |
478 << std::string(net::ErrorToString(net_result)); | |
479 ui_task_runner()->PostTask(FROM_HERE, base::Bind(callback, REJECTED)); | |
480 return; | |
481 } | |
482 | |
483 VLOG(2) << uuid_.canonical_value() | |
484 << ": Taking descriptor, confirming success."; | |
485 fd->TakeValue(); | |
486 ui_task_runner()->PostTask(FROM_HERE, base::Bind(callback, SUCCESS)); | |
487 } | |
488 | |
489 void BluetoothSocketBlueZ::OnNewConnection( | |
490 scoped_refptr<BluetoothSocket> socket, | |
491 const ConfirmationCallback& callback, | |
492 Status status) { | |
493 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
494 DCHECK(accept_request_.get()); | |
495 DCHECK(connection_request_queue_.size() >= 1); | |
496 | |
497 linked_ptr<ConnectionRequest> request = connection_request_queue_.front(); | |
498 if (status == SUCCESS && !request->cancelled) { | |
499 BluetoothDeviceBlueZ* device = | |
500 static_cast<BluetoothAdapterBlueZ*>(adapter_.get()) | |
501 ->GetDeviceWithPath(request->device_path); | |
502 DCHECK(device); | |
503 | |
504 accept_request_->success_callback.Run(device, socket); | |
505 } else { | |
506 accept_request_->error_callback.Run(kAcceptFailed); | |
507 } | |
508 | |
509 accept_request_.reset(nullptr); | |
510 connection_request_queue_.pop(); | |
511 | |
512 callback.Run(status); | |
513 } | |
514 | |
515 void BluetoothSocketBlueZ::DoCloseListening() { | |
516 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
517 | |
518 if (accept_request_) { | |
519 accept_request_->error_callback.Run( | |
520 net::ErrorToString(net::ERR_CONNECTION_CLOSED)); | |
521 accept_request_.reset(nullptr); | |
522 } | |
523 | |
524 while (connection_request_queue_.size() > 0) { | |
525 linked_ptr<ConnectionRequest> request = connection_request_queue_.front(); | |
526 request->callback.Run(REJECTED); | |
527 connection_request_queue_.pop(); | |
528 } | |
529 } | |
530 | |
531 void BluetoothSocketBlueZ::UnregisterProfile() { | |
532 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); | |
533 DCHECK(profile_); | |
534 | |
535 VLOG(1) << profile_->object_path().value() << ": Release profile"; | |
536 | |
537 static_cast<BluetoothAdapterBlueZ*>(adapter_.get()) | |
538 ->ReleaseProfile(device_path_, profile_); | |
539 profile_ = nullptr; | |
540 } | |
541 | |
542 } // namespace bluez | |
OLD | NEW |