Roll src/third_party/WebKit 787a07c:716df21 (svn 201034:201036)
[chromium-blink-merge.git] / device / bluetooth / bluetooth_socket_chromeos.cc
blobda15484c388032da8a23423f3110e6a200a07757
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.
5 #include "device/bluetooth/bluetooth_socket_chromeos.h"
7 #include <queue>
8 #include <string>
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/message_loop/message_loop.h"
18 #include "base/sequenced_task_runner.h"
19 #include "base/strings/string_util.h"
20 #include "base/task_runner_util.h"
21 #include "base/threading/thread_restrictions.h"
22 #include "base/threading/worker_pool.h"
23 #include "chromeos/dbus/bluetooth_device_client.h"
24 #include "chromeos/dbus/bluetooth_profile_manager_client.h"
25 #include "chromeos/dbus/bluetooth_profile_service_provider.h"
26 #include "chromeos/dbus/dbus_thread_manager.h"
27 #include "dbus/bus.h"
28 #include "dbus/file_descriptor.h"
29 #include "dbus/object_path.h"
30 #include "device/bluetooth/bluetooth_adapter.h"
31 #include "device/bluetooth/bluetooth_adapter_chromeos.h"
32 #include "device/bluetooth/bluetooth_adapter_profile_chromeos.h"
33 #include "device/bluetooth/bluetooth_device.h"
34 #include "device/bluetooth/bluetooth_device_chromeos.h"
35 #include "device/bluetooth/bluetooth_socket.h"
36 #include "device/bluetooth/bluetooth_socket_net.h"
37 #include "device/bluetooth/bluetooth_socket_thread.h"
38 #include "net/base/ip_endpoint.h"
39 #include "net/base/net_errors.h"
40 #include "third_party/cros_system_api/dbus/service_constants.h"
42 using device::BluetoothAdapter;
43 using device::BluetoothDevice;
44 using device::BluetoothSocketThread;
45 using device::BluetoothUUID;
47 namespace {
49 const char kAcceptFailed[] = "Failed to accept connection.";
50 const char kInvalidUUID[] = "Invalid UUID";
51 const char kSocketNotListening[] = "Socket is not listening.";
53 } // namespace
55 namespace chromeos {
57 // static
58 scoped_refptr<BluetoothSocketChromeOS>
59 BluetoothSocketChromeOS::CreateBluetoothSocket(
60 scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
61 scoped_refptr<BluetoothSocketThread> socket_thread) {
62 DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
64 return make_scoped_refptr(
65 new BluetoothSocketChromeOS(ui_task_runner, socket_thread));
68 BluetoothSocketChromeOS::AcceptRequest::AcceptRequest() {}
70 BluetoothSocketChromeOS::AcceptRequest::~AcceptRequest() {}
72 BluetoothSocketChromeOS::ConnectionRequest::ConnectionRequest()
73 : accepting(false),
74 cancelled(false) {}
76 BluetoothSocketChromeOS::ConnectionRequest::~ConnectionRequest() {}
78 BluetoothSocketChromeOS::BluetoothSocketChromeOS(
79 scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
80 scoped_refptr<BluetoothSocketThread> socket_thread)
81 : BluetoothSocketNet(ui_task_runner, socket_thread), profile_(nullptr) {
84 BluetoothSocketChromeOS::~BluetoothSocketChromeOS() {
85 DCHECK(!profile_);
87 if (adapter_.get()) {
88 adapter_->RemoveObserver(this);
89 adapter_ = nullptr;
93 void BluetoothSocketChromeOS::Connect(
94 const BluetoothDeviceChromeOS* device,
95 const BluetoothUUID& uuid,
96 SecurityLevel security_level,
97 const base::Closure& success_callback,
98 const ErrorCompletionCallback& error_callback) {
99 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
100 DCHECK(!profile_);
102 if (!uuid.IsValid()) {
103 error_callback.Run(kInvalidUUID);
104 return;
107 device_address_ = device->GetAddress();
108 device_path_ = device->object_path();
109 uuid_ = uuid;
110 options_.reset(new BluetoothProfileManagerClient::Options());
111 if (security_level == SECURITY_LEVEL_LOW)
112 options_->require_authentication.reset(new bool(false));
114 adapter_ = device->adapter();
116 RegisterProfile(device->adapter(), success_callback, error_callback);
119 void BluetoothSocketChromeOS::Listen(
120 scoped_refptr<BluetoothAdapter> adapter,
121 SocketType socket_type,
122 const BluetoothUUID& uuid,
123 const BluetoothAdapter::ServiceOptions& service_options,
124 const base::Closure& success_callback,
125 const ErrorCompletionCallback& error_callback) {
126 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
127 DCHECK(!profile_);
129 if (!uuid.IsValid()) {
130 error_callback.Run(kInvalidUUID);
131 return;
134 adapter_ = adapter;
135 adapter_->AddObserver(this);
137 uuid_ = uuid;
138 options_.reset(new BluetoothProfileManagerClient::Options());
139 if (service_options.name)
140 options_->name.reset(new std::string(*service_options.name));
142 switch (socket_type) {
143 case kRfcomm:
144 options_->channel.reset(
145 new uint16(service_options.channel ? *service_options.channel : 0));
146 break;
147 case kL2cap:
148 options_->psm.reset(
149 new uint16(service_options.psm ? *service_options.psm : 0));
150 break;
151 default:
152 NOTREACHED();
155 RegisterProfile(static_cast<BluetoothAdapterChromeOS*>(adapter.get()),
156 success_callback, error_callback);
159 void BluetoothSocketChromeOS::Close() {
160 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
162 if (profile_)
163 UnregisterProfile();
165 // In the case below, where an asynchronous task gets posted on the socket
166 // thread in BluetoothSocketNet::Close, a reference will be held to this
167 // socket by the callback. This may cause the BluetoothAdapter to outlive
168 // DBusThreadManager during shutdown if that callback executes too late.
169 if (adapter_.get()) {
170 adapter_->RemoveObserver(this);
171 adapter_ = nullptr;
174 if (!device_path_.value().empty()) {
175 BluetoothSocketNet::Close();
176 } else {
177 DoCloseListening();
181 void BluetoothSocketChromeOS::Disconnect(const base::Closure& callback) {
182 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
184 if (profile_)
185 UnregisterProfile();
187 if (!device_path_.value().empty()) {
188 BluetoothSocketNet::Disconnect(callback);
189 } else {
190 DoCloseListening();
191 callback.Run();
195 void BluetoothSocketChromeOS::Accept(
196 const AcceptCompletionCallback& success_callback,
197 const ErrorCompletionCallback& error_callback) {
198 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
200 if (!device_path_.value().empty()) {
201 error_callback.Run(kSocketNotListening);
202 return;
205 // Only one pending accept at a time
206 if (accept_request_.get()) {
207 error_callback.Run(net::ErrorToString(net::ERR_IO_PENDING));
208 return;
211 accept_request_.reset(new AcceptRequest);
212 accept_request_->success_callback = success_callback;
213 accept_request_->error_callback = error_callback;
215 if (connection_request_queue_.size() >= 1) {
216 AcceptConnectionRequest();
220 void BluetoothSocketChromeOS::RegisterProfile(
221 BluetoothAdapterChromeOS* adapter,
222 const base::Closure& success_callback,
223 const ErrorCompletionCallback& error_callback) {
224 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
225 DCHECK(!profile_);
226 DCHECK(adapter);
228 // If the adapter is not present, this is a listening socket and the
229 // adapter isn't running yet. Report success and carry on;
230 // the profile will be registered when the daemon becomes available.
231 if (!adapter->IsPresent()) {
232 VLOG(1) << uuid_.canonical_value() << " on " << device_path_.value()
233 << ": Delaying profile registration.";
234 base::MessageLoop::current()->PostTask(FROM_HERE, success_callback);
235 return;
238 VLOG(1) << uuid_.canonical_value() << " on " << device_path_.value()
239 << ": Acquiring profile.";
241 adapter->UseProfile(
242 uuid_, device_path_, *options_, this,
243 base::Bind(&BluetoothSocketChromeOS::OnRegisterProfile, this,
244 success_callback, error_callback),
245 base::Bind(&BluetoothSocketChromeOS::OnRegisterProfileError, this,
246 error_callback));
249 void BluetoothSocketChromeOS::OnRegisterProfile(
250 const base::Closure& success_callback,
251 const ErrorCompletionCallback& error_callback,
252 BluetoothAdapterProfileChromeOS* profile) {
253 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
254 DCHECK(!profile_);
256 profile_ = profile;
258 if (device_path_.value().empty()) {
259 VLOG(1) << uuid_.canonical_value() << ": Profile registered.";
260 success_callback.Run();
261 return;
264 VLOG(1) << uuid_.canonical_value() << ": Got profile, connecting to "
265 << device_path_.value();
267 DBusThreadManager::Get()->GetBluetoothDeviceClient()->ConnectProfile(
268 device_path_, uuid_.canonical_value(),
269 base::Bind(&BluetoothSocketChromeOS::OnConnectProfile, this,
270 success_callback),
271 base::Bind(&BluetoothSocketChromeOS::OnConnectProfileError, this,
272 error_callback));
275 void BluetoothSocketChromeOS::OnRegisterProfileError(
276 const ErrorCompletionCallback& error_callback,
277 const std::string& error_message) {
278 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
280 LOG(WARNING) << uuid_.canonical_value()
281 << ": Failed to register profile: " << error_message;
282 error_callback.Run(error_message);
285 void BluetoothSocketChromeOS::OnConnectProfile(
286 const base::Closure& success_callback) {
287 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
288 DCHECK(profile_);
290 VLOG(1) << profile_->object_path().value() << ": Profile connected.";
291 UnregisterProfile();
292 success_callback.Run();
295 void BluetoothSocketChromeOS::OnConnectProfileError(
296 const ErrorCompletionCallback& error_callback,
297 const std::string& error_name,
298 const std::string& error_message) {
299 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
300 DCHECK(profile_);
302 LOG(WARNING) << profile_->object_path().value()
303 << ": Failed to connect profile: " << error_name << ": "
304 << error_message;
305 UnregisterProfile();
306 error_callback.Run(error_message);
309 void BluetoothSocketChromeOS::AdapterPresentChanged(BluetoothAdapter* adapter,
310 bool present) {
311 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
313 if (!present) {
314 // Adapter removed, we can't use the profile anymore.
315 UnregisterProfile();
316 return;
319 DCHECK(!profile_);
321 VLOG(1) << uuid_.canonical_value() << " on " << device_path_.value()
322 << ": Acquiring profile.";
324 static_cast<BluetoothAdapterChromeOS*>(adapter)->UseProfile(
325 uuid_, device_path_, *options_, this,
326 base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfile, this),
327 base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfileError,
328 this));
331 void BluetoothSocketChromeOS::OnInternalRegisterProfile(
332 BluetoothAdapterProfileChromeOS* profile) {
333 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
334 DCHECK(!profile_);
336 profile_ = profile;
338 VLOG(1) << uuid_.canonical_value() << ": Profile re-registered";
341 void BluetoothSocketChromeOS::OnInternalRegisterProfileError(
342 const std::string& error_message) {
343 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
345 LOG(WARNING) << "Failed to re-register profile: " << error_message;
348 void BluetoothSocketChromeOS::Released() {
349 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
350 DCHECK(profile_);
352 VLOG(1) << profile_->object_path().value() << ": Release";
355 void BluetoothSocketChromeOS::NewConnection(
356 const dbus::ObjectPath& device_path,
357 scoped_ptr<dbus::FileDescriptor> fd,
358 const BluetoothProfileServiceProvider::Delegate::Options& options,
359 const ConfirmationCallback& callback) {
360 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
362 VLOG(1) << uuid_.canonical_value()
363 << ": New connection from device: " << device_path.value();
365 if (!device_path_.value().empty()) {
366 DCHECK(device_path_ == device_path);
368 socket_thread()->task_runner()->PostTask(
369 FROM_HERE,
370 base::Bind(
371 &BluetoothSocketChromeOS::DoNewConnection,
372 this,
373 device_path_,
374 base::Passed(&fd),
375 options,
376 callback));
377 } else {
378 linked_ptr<ConnectionRequest> request(new ConnectionRequest());
379 request->device_path = device_path;
380 request->fd = fd.Pass();
381 request->options = options;
382 request->callback = callback;
384 connection_request_queue_.push(request);
385 VLOG(1) << uuid_.canonical_value() << ": Connection is now pending.";
386 if (accept_request_) {
387 AcceptConnectionRequest();
392 void BluetoothSocketChromeOS::RequestDisconnection(
393 const dbus::ObjectPath& device_path,
394 const ConfirmationCallback& callback) {
395 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
396 DCHECK(profile_);
398 VLOG(1) << profile_->object_path().value() << ": Request disconnection";
399 callback.Run(SUCCESS);
402 void BluetoothSocketChromeOS::Cancel() {
403 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
404 DCHECK(profile_);
406 VLOG(1) << profile_->object_path().value() << ": Cancel";
408 if (!connection_request_queue_.size())
409 return;
411 // If the front request is being accepted mark it as cancelled, otherwise
412 // just pop it from the queue.
413 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
414 if (!request->accepting) {
415 request->cancelled = true;
416 } else {
417 connection_request_queue_.pop();
421 void BluetoothSocketChromeOS::AcceptConnectionRequest() {
422 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
423 DCHECK(accept_request_.get());
424 DCHECK(connection_request_queue_.size() >= 1);
425 DCHECK(profile_);
427 VLOG(1) << profile_->object_path().value()
428 << ": Accepting pending connection.";
430 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
431 request->accepting = true;
433 BluetoothDeviceChromeOS* device =
434 static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
435 GetDeviceWithPath(request->device_path);
436 DCHECK(device);
438 scoped_refptr<BluetoothSocketChromeOS> client_socket =
439 BluetoothSocketChromeOS::CreateBluetoothSocket(
440 ui_task_runner(), socket_thread());
442 client_socket->device_address_ = device->GetAddress();
443 client_socket->device_path_ = request->device_path;
444 client_socket->uuid_ = uuid_;
446 socket_thread()->task_runner()->PostTask(
447 FROM_HERE,
448 base::Bind(
449 &BluetoothSocketChromeOS::DoNewConnection,
450 client_socket,
451 request->device_path,
452 base::Passed(&request->fd),
453 request->options,
454 base::Bind(&BluetoothSocketChromeOS::OnNewConnection,
455 this,
456 client_socket,
457 request->callback)));
460 void BluetoothSocketChromeOS::DoNewConnection(
461 const dbus::ObjectPath& device_path,
462 scoped_ptr<dbus::FileDescriptor> fd,
463 const BluetoothProfileServiceProvider::Delegate::Options& options,
464 const ConfirmationCallback& callback) {
465 DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
466 base::ThreadRestrictions::AssertIOAllowed();
467 fd->CheckValidity();
469 VLOG(1) << uuid_.canonical_value() << ": Validity check complete.";
470 if (!fd->is_valid()) {
471 LOG(WARNING) << uuid_.canonical_value() << " :" << fd->value()
472 << ": Invalid file descriptor received from Bluetooth Daemon.";
473 ui_task_runner()->PostTask(FROM_HERE,
474 base::Bind(callback, REJECTED));;
475 return;
478 if (tcp_socket()) {
479 LOG(WARNING) << uuid_.canonical_value() << ": Already connected";
480 ui_task_runner()->PostTask(FROM_HERE,
481 base::Bind(callback, REJECTED));;
482 return;
485 ResetTCPSocket();
487 // Note: We don't have a meaningful |IPEndPoint|, but that is ok since the
488 // TCPSocket implementation does not actually require one.
489 int net_result = tcp_socket()->AdoptConnectedSocket(fd->value(),
490 net::IPEndPoint());
491 if (net_result != net::OK) {
492 LOG(WARNING) << uuid_.canonical_value() << ": Error adopting socket: "
493 << std::string(net::ErrorToString(net_result));
494 ui_task_runner()->PostTask(FROM_HERE,
495 base::Bind(callback, REJECTED));;
496 return;
499 VLOG(2) << uuid_.canonical_value()
500 << ": Taking descriptor, confirming success.";
501 fd->TakeValue();
502 ui_task_runner()->PostTask(FROM_HERE,
503 base::Bind(callback, SUCCESS));;
506 void BluetoothSocketChromeOS::OnNewConnection(
507 scoped_refptr<BluetoothSocket> socket,
508 const ConfirmationCallback& callback,
509 Status status) {
510 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
511 DCHECK(accept_request_.get());
512 DCHECK(connection_request_queue_.size() >= 1);
514 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
515 if (status == SUCCESS && !request->cancelled) {
516 BluetoothDeviceChromeOS* device =
517 static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
518 GetDeviceWithPath(request->device_path);
519 DCHECK(device);
521 accept_request_->success_callback.Run(device, socket);
522 } else {
523 accept_request_->error_callback.Run(kAcceptFailed);
526 accept_request_.reset(nullptr);
527 connection_request_queue_.pop();
529 callback.Run(status);
532 void BluetoothSocketChromeOS::DoCloseListening() {
533 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
535 if (accept_request_) {
536 accept_request_->error_callback.Run(
537 net::ErrorToString(net::ERR_CONNECTION_CLOSED));
538 accept_request_.reset(nullptr);
541 while (connection_request_queue_.size() > 0) {
542 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
543 request->callback.Run(REJECTED);
544 connection_request_queue_.pop();
548 void BluetoothSocketChromeOS::UnregisterProfile() {
549 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
550 DCHECK(profile_);
552 VLOG(1) << profile_->object_path().value() << ": Release profile";
554 static_cast<BluetoothAdapterChromeOS*>(adapter_.get())
555 ->ReleaseProfile(device_path_, profile_);
556 profile_ = nullptr;
559 } // namespace chromeos