Make castv2 performance test work.
[chromium-blink-merge.git] / device / bluetooth / bluetooth_socket_chromeos.cc
blobc193c1f1d906f83972d880403205d6d823015969
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/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_adapter_profile_chromeos.h"
32 #include "device/bluetooth/bluetooth_device.h"
33 #include "device/bluetooth/bluetooth_device_chromeos.h"
34 #include "device/bluetooth/bluetooth_socket.h"
35 #include "device/bluetooth/bluetooth_socket_net.h"
36 #include "device/bluetooth/bluetooth_socket_thread.h"
37 #include "net/base/ip_endpoint.h"
38 #include "net/base/net_errors.h"
39 #include "third_party/cros_system_api/dbus/service_constants.h"
41 using device::BluetoothAdapter;
42 using device::BluetoothDevice;
43 using device::BluetoothSocketThread;
44 using device::BluetoothUUID;
46 namespace {
48 const char kAcceptFailed[] = "Failed to accept connection.";
49 const char kInvalidUUID[] = "Invalid UUID";
50 const char kSocketNotListening[] = "Socket is not listening.";
52 } // namespace
54 namespace chromeos {
56 // static
57 scoped_refptr<BluetoothSocketChromeOS>
58 BluetoothSocketChromeOS::CreateBluetoothSocket(
59 scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
60 scoped_refptr<BluetoothSocketThread> socket_thread) {
61 DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
63 return make_scoped_refptr(
64 new BluetoothSocketChromeOS(ui_task_runner, socket_thread));
67 BluetoothSocketChromeOS::AcceptRequest::AcceptRequest() {}
69 BluetoothSocketChromeOS::AcceptRequest::~AcceptRequest() {}
71 BluetoothSocketChromeOS::ConnectionRequest::ConnectionRequest()
72 : accepting(false),
73 cancelled(false) {}
75 BluetoothSocketChromeOS::ConnectionRequest::~ConnectionRequest() {}
77 BluetoothSocketChromeOS::BluetoothSocketChromeOS(
78 scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
79 scoped_refptr<BluetoothSocketThread> socket_thread)
80 : BluetoothSocketNet(ui_task_runner, socket_thread), profile_(nullptr) {
83 BluetoothSocketChromeOS::~BluetoothSocketChromeOS() {
84 DCHECK(!profile_);
86 if (adapter_.get()) {
87 adapter_->RemoveObserver(this);
88 adapter_ = NULL;
92 void BluetoothSocketChromeOS::Connect(
93 const BluetoothDeviceChromeOS* 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_);
101 if (!uuid.IsValid()) {
102 error_callback.Run(kInvalidUUID);
103 return;
106 device_address_ = device->GetAddress();
107 device_path_ = device->object_path();
108 uuid_ = uuid;
109 options_.reset(new BluetoothProfileManagerClient::Options());
110 if (security_level == SECURITY_LEVEL_LOW)
111 options_->require_authentication.reset(new bool(false));
113 adapter_ = device->adapter();
115 RegisterProfile(device->adapter(), success_callback, error_callback);
118 void BluetoothSocketChromeOS::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_);
128 if (!uuid.IsValid()) {
129 error_callback.Run(kInvalidUUID);
130 return;
133 adapter_ = adapter;
134 adapter_->AddObserver(this);
136 uuid_ = uuid;
137 options_.reset(new BluetoothProfileManagerClient::Options());
138 if (service_options.name)
139 options_->name.reset(new std::string(*service_options.name));
141 switch (socket_type) {
142 case kRfcomm:
143 options_->channel.reset(
144 new uint16(service_options.channel ? *service_options.channel : 0));
145 break;
146 case kL2cap:
147 options_->psm.reset(
148 new uint16(service_options.psm ? *service_options.psm : 0));
149 break;
150 default:
151 NOTREACHED();
154 RegisterProfile(static_cast<BluetoothAdapterChromeOS*>(adapter.get()),
155 success_callback, error_callback);
158 void BluetoothSocketChromeOS::Close() {
159 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
161 if (profile_)
162 UnregisterProfile();
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 // DBusThreadManager during shutdown if that callback executes too late.
168 if (adapter_.get()) {
169 adapter_->RemoveObserver(this);
170 adapter_ = NULL;
173 if (!device_path_.value().empty()) {
174 BluetoothSocketNet::Close();
175 } else {
176 DoCloseListening();
180 void BluetoothSocketChromeOS::Disconnect(const base::Closure& callback) {
181 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
183 if (profile_)
184 UnregisterProfile();
186 if (!device_path_.value().empty()) {
187 BluetoothSocketNet::Disconnect(callback);
188 } else {
189 DoCloseListening();
190 callback.Run();
194 void BluetoothSocketChromeOS::Accept(
195 const AcceptCompletionCallback& success_callback,
196 const ErrorCompletionCallback& error_callback) {
197 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
199 if (!device_path_.value().empty()) {
200 error_callback.Run(kSocketNotListening);
201 return;
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;
210 accept_request_.reset(new AcceptRequest);
211 accept_request_->success_callback = success_callback;
212 accept_request_->error_callback = error_callback;
214 if (connection_request_queue_.size() >= 1) {
215 AcceptConnectionRequest();
219 void BluetoothSocketChromeOS::RegisterProfile(
220 BluetoothAdapterChromeOS* adapter,
221 const base::Closure& success_callback,
222 const ErrorCompletionCallback& error_callback) {
223 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
224 DCHECK(!profile_);
225 DCHECK(adapter);
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;
237 VLOG(1) << uuid_.canonical_value() << " on " << device_path_.value()
238 << ": Acquiring profile.";
240 adapter->UseProfile(
241 uuid_, device_path_, *options_, this,
242 base::Bind(&BluetoothSocketChromeOS::OnRegisterProfile, this,
243 success_callback, error_callback),
244 base::Bind(&BluetoothSocketChromeOS::OnRegisterProfileError, this,
245 error_callback));
248 void BluetoothSocketChromeOS::OnRegisterProfile(
249 const base::Closure& success_callback,
250 const ErrorCompletionCallback& error_callback,
251 BluetoothAdapterProfileChromeOS* profile) {
252 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
253 DCHECK(!profile_);
255 profile_ = profile;
257 if (device_path_.value().empty()) {
258 VLOG(1) << uuid_.canonical_value() << ": Profile registered.";
259 success_callback.Run();
260 return;
263 VLOG(1) << uuid_.canonical_value() << ": Got profile, connecting to "
264 << device_path_.value();
266 DBusThreadManager::Get()->GetBluetoothDeviceClient()->ConnectProfile(
267 device_path_, uuid_.canonical_value(),
268 base::Bind(&BluetoothSocketChromeOS::OnConnectProfile, this,
269 success_callback),
270 base::Bind(&BluetoothSocketChromeOS::OnConnectProfileError, this,
271 error_callback));
274 void BluetoothSocketChromeOS::OnRegisterProfileError(
275 const ErrorCompletionCallback& error_callback,
276 const std::string& error_message) {
277 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
279 LOG(WARNING) << uuid_.canonical_value()
280 << ": Failed to register profile: " << error_message;
281 error_callback.Run(error_message);
284 void BluetoothSocketChromeOS::OnConnectProfile(
285 const base::Closure& success_callback) {
286 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
287 DCHECK(profile_);
289 VLOG(1) << profile_->object_path().value() << ": Profile connected.";
290 UnregisterProfile();
291 success_callback.Run();
294 void BluetoothSocketChromeOS::OnConnectProfileError(
295 const ErrorCompletionCallback& error_callback,
296 const std::string& error_name,
297 const std::string& error_message) {
298 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
299 DCHECK(profile_);
301 LOG(WARNING) << profile_->object_path().value()
302 << ": Failed to connect profile: " << error_name << ": "
303 << error_message;
304 UnregisterProfile();
305 error_callback.Run(error_message);
308 void BluetoothSocketChromeOS::AdapterPresentChanged(BluetoothAdapter* adapter,
309 bool present) {
310 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
312 if (!present) {
313 // Adapter removed, we can't use the profile anymore.
314 UnregisterProfile();
315 return;
318 DCHECK(!profile_);
320 VLOG(1) << uuid_.canonical_value() << " on " << device_path_.value()
321 << ": Acquiring profile.";
323 static_cast<BluetoothAdapterChromeOS*>(adapter)->UseProfile(
324 uuid_, device_path_, *options_, this,
325 base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfile, this),
326 base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfileError,
327 this));
330 void BluetoothSocketChromeOS::OnInternalRegisterProfile(
331 BluetoothAdapterProfileChromeOS* profile) {
332 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
333 DCHECK(!profile_);
335 profile_ = profile;
337 VLOG(1) << uuid_.canonical_value() << ": Profile re-registered";
340 void BluetoothSocketChromeOS::OnInternalRegisterProfileError(
341 const std::string& error_message) {
342 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
344 LOG(WARNING) << "Failed to re-register profile: " << error_message;
347 void BluetoothSocketChromeOS::Released() {
348 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
349 DCHECK(profile_);
351 VLOG(1) << profile_->object_path().value() << ": Release";
354 void BluetoothSocketChromeOS::NewConnection(
355 const dbus::ObjectPath& device_path,
356 scoped_ptr<dbus::FileDescriptor> fd,
357 const BluetoothProfileServiceProvider::Delegate::Options& options,
358 const ConfirmationCallback& callback) {
359 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
361 VLOG(1) << uuid_.canonical_value()
362 << ": New connection from device: " << device_path.value();
364 if (!device_path_.value().empty()) {
365 DCHECK(device_path_ == device_path);
367 socket_thread()->task_runner()->PostTask(
368 FROM_HERE,
369 base::Bind(
370 &BluetoothSocketChromeOS::DoNewConnection,
371 this,
372 device_path_,
373 base::Passed(&fd),
374 options,
375 callback));
376 } else {
377 linked_ptr<ConnectionRequest> request(new ConnectionRequest());
378 request->device_path = device_path;
379 request->fd = fd.Pass();
380 request->options = options;
381 request->callback = callback;
383 connection_request_queue_.push(request);
384 VLOG(1) << uuid_.canonical_value() << ": Connection is now pending.";
385 if (accept_request_) {
386 AcceptConnectionRequest();
391 void BluetoothSocketChromeOS::RequestDisconnection(
392 const dbus::ObjectPath& device_path,
393 const ConfirmationCallback& callback) {
394 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
395 DCHECK(profile_);
397 VLOG(1) << profile_->object_path().value() << ": Request disconnection";
398 callback.Run(SUCCESS);
401 void BluetoothSocketChromeOS::Cancel() {
402 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
403 DCHECK(profile_);
405 VLOG(1) << profile_->object_path().value() << ": Cancel";
407 if (!connection_request_queue_.size())
408 return;
410 // If the front request is being accepted mark it as cancelled, otherwise
411 // just pop it from the queue.
412 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
413 if (!request->accepting) {
414 request->cancelled = true;
415 } else {
416 connection_request_queue_.pop();
420 void BluetoothSocketChromeOS::AcceptConnectionRequest() {
421 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
422 DCHECK(accept_request_.get());
423 DCHECK(connection_request_queue_.size() >= 1);
424 DCHECK(profile_);
426 VLOG(1) << profile_->object_path().value()
427 << ": Accepting pending connection.";
429 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
430 request->accepting = true;
432 BluetoothDeviceChromeOS* device =
433 static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
434 GetDeviceWithPath(request->device_path);
435 DCHECK(device);
437 scoped_refptr<BluetoothSocketChromeOS> client_socket =
438 BluetoothSocketChromeOS::CreateBluetoothSocket(
439 ui_task_runner(), socket_thread());
441 client_socket->device_address_ = device->GetAddress();
442 client_socket->device_path_ = request->device_path;
443 client_socket->uuid_ = uuid_;
445 socket_thread()->task_runner()->PostTask(
446 FROM_HERE,
447 base::Bind(
448 &BluetoothSocketChromeOS::DoNewConnection,
449 client_socket,
450 request->device_path,
451 base::Passed(&request->fd),
452 request->options,
453 base::Bind(&BluetoothSocketChromeOS::OnNewConnection,
454 this,
455 client_socket,
456 request->callback)));
459 void BluetoothSocketChromeOS::DoNewConnection(
460 const dbus::ObjectPath& device_path,
461 scoped_ptr<dbus::FileDescriptor> fd,
462 const BluetoothProfileServiceProvider::Delegate::Options& options,
463 const ConfirmationCallback& callback) {
464 DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
465 base::ThreadRestrictions::AssertIOAllowed();
466 fd->CheckValidity();
468 VLOG(1) << uuid_.canonical_value() << ": Validity check complete.";
469 if (!fd->is_valid()) {
470 LOG(WARNING) << uuid_.canonical_value() << " :" << fd->value()
471 << ": Invalid file descriptor received from Bluetooth Daemon.";
472 ui_task_runner()->PostTask(FROM_HERE,
473 base::Bind(callback, REJECTED));;
474 return;
477 if (tcp_socket()) {
478 LOG(WARNING) << uuid_.canonical_value() << ": Already connected";
479 ui_task_runner()->PostTask(FROM_HERE,
480 base::Bind(callback, REJECTED));;
481 return;
484 ResetTCPSocket();
486 // Note: We don't have a meaningful |IPEndPoint|, but that is ok since the
487 // TCPSocket implementation does not actually require one.
488 int net_result = tcp_socket()->AdoptConnectedSocket(fd->value(),
489 net::IPEndPoint());
490 if (net_result != net::OK) {
491 LOG(WARNING) << uuid_.canonical_value() << ": Error adopting socket: "
492 << std::string(net::ErrorToString(net_result));
493 ui_task_runner()->PostTask(FROM_HERE,
494 base::Bind(callback, REJECTED));;
495 return;
498 VLOG(2) << uuid_.canonical_value()
499 << ": Taking descriptor, confirming success.";
500 fd->TakeValue();
501 ui_task_runner()->PostTask(FROM_HERE,
502 base::Bind(callback, SUCCESS));;
505 void BluetoothSocketChromeOS::OnNewConnection(
506 scoped_refptr<BluetoothSocket> socket,
507 const ConfirmationCallback& callback,
508 Status status) {
509 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
510 DCHECK(accept_request_.get());
511 DCHECK(connection_request_queue_.size() >= 1);
513 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
514 if (status == SUCCESS && !request->cancelled) {
515 BluetoothDeviceChromeOS* device =
516 static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
517 GetDeviceWithPath(request->device_path);
518 DCHECK(device);
520 accept_request_->success_callback.Run(device, socket);
521 } else {
522 accept_request_->error_callback.Run(kAcceptFailed);
525 accept_request_.reset(NULL);
526 connection_request_queue_.pop();
528 callback.Run(status);
531 void BluetoothSocketChromeOS::DoCloseListening() {
532 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
534 if (accept_request_) {
535 accept_request_->error_callback.Run(
536 net::ErrorToString(net::ERR_CONNECTION_CLOSED));
537 accept_request_.reset(NULL);
540 while (connection_request_queue_.size() > 0) {
541 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
542 request->callback.Run(REJECTED);
543 connection_request_queue_.pop();
547 void BluetoothSocketChromeOS::UnregisterProfile() {
548 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
549 DCHECK(profile_);
551 VLOG(1) << profile_->object_path().value() << ": Release profile";
553 static_cast<BluetoothAdapterChromeOS*>(adapter_.get())
554 ->ReleaseProfile(device_path_, profile_);
555 profile_ = nullptr;
558 } // namespace chromeos