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