Some additional network settings cleanup
[chromium-blink-merge.git] / device / bluetooth / bluetooth_socket_chromeos.cc
blob5eedf5b1c5a27b0028f0502067888ebb9f8a36da
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_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"
40 using device::BluetoothAdapter;
41 using device::BluetoothDevice;
42 using device::BluetoothSocketThread;
43 using device::BluetoothUUID;
45 namespace {
47 const char kAcceptFailed[] = "Failed to accept connection.";
48 const char kInvalidUUID[] = "Invalid UUID";
49 const char kSocketNotListening[] = "Socket is not listening.";
51 } // namespace
53 namespace chromeos {
55 // static
56 scoped_refptr<BluetoothSocketChromeOS>
57 BluetoothSocketChromeOS::CreateBluetoothSocket(
58 scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
59 scoped_refptr<BluetoothSocketThread> socket_thread) {
60 DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
62 return make_scoped_refptr(
63 new BluetoothSocketChromeOS(ui_task_runner, socket_thread));
66 BluetoothSocketChromeOS::AcceptRequest::AcceptRequest() {}
68 BluetoothSocketChromeOS::AcceptRequest::~AcceptRequest() {}
70 BluetoothSocketChromeOS::ConnectionRequest::ConnectionRequest()
71 : accepting(false),
72 cancelled(false) {}
74 BluetoothSocketChromeOS::ConnectionRequest::~ConnectionRequest() {}
76 BluetoothSocketChromeOS::BluetoothSocketChromeOS(
77 scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
78 scoped_refptr<BluetoothSocketThread> socket_thread)
79 : BluetoothSocketNet(ui_task_runner, socket_thread) {
82 BluetoothSocketChromeOS::~BluetoothSocketChromeOS() {
83 DCHECK(object_path_.value().empty());
84 DCHECK(profile_.get() == NULL);
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(object_path_.value().empty());
100 DCHECK(!profile_.get());
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 RegisterProfile(success_callback, error_callback);
117 void BluetoothSocketChromeOS::Listen(
118 scoped_refptr<BluetoothAdapter> adapter,
119 SocketType socket_type,
120 const BluetoothUUID& uuid,
121 const BluetoothAdapter::ServiceOptions& service_options,
122 const base::Closure& success_callback,
123 const ErrorCompletionCallback& error_callback) {
124 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
125 DCHECK(object_path_.value().empty());
126 DCHECK(!profile_.get());
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(success_callback, error_callback);
157 void BluetoothSocketChromeOS::Close() {
158 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
160 if (profile_)
161 UnregisterProfile();
163 // In the case below, where an asynchronous task gets posted on the socket
164 // thread in BluetoothSocketNet::Close, a reference will be held to this
165 // socket by the callback. This may cause the BluetoothAdapter to outlive
166 // DBusThreadManager during shutdown if that callback executes too late.
167 if (adapter_.get()) {
168 adapter_->RemoveObserver(this);
169 adapter_ = NULL;
172 if (!device_path_.value().empty()) {
173 BluetoothSocketNet::Close();
174 } else {
175 DoCloseListening();
179 void BluetoothSocketChromeOS::Disconnect(const base::Closure& callback) {
180 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
182 if (profile_)
183 UnregisterProfile();
185 if (!device_path_.value().empty()) {
186 BluetoothSocketNet::Disconnect(callback);
187 } else {
188 DoCloseListening();
189 callback.Run();
193 void BluetoothSocketChromeOS::Accept(
194 const AcceptCompletionCallback& success_callback,
195 const ErrorCompletionCallback& error_callback) {
196 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
198 if (!device_path_.value().empty()) {
199 error_callback.Run(kSocketNotListening);
200 return;
203 // Only one pending accept at a time
204 if (accept_request_.get()) {
205 error_callback.Run(net::ErrorToString(net::ERR_IO_PENDING));
206 return;
209 accept_request_.reset(new AcceptRequest);
210 accept_request_->success_callback = success_callback;
211 accept_request_->error_callback = error_callback;
213 if (connection_request_queue_.size() >= 1) {
214 AcceptConnectionRequest();
218 void BluetoothSocketChromeOS::RegisterProfile(
219 const base::Closure& success_callback,
220 const ErrorCompletionCallback& error_callback) {
221 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
222 DCHECK(object_path_.value().empty());
223 DCHECK(!profile_.get());
225 // The object path is relatively meaningless, but has to be unique, so for
226 // connecting profiles use a combination of the device address and profile
227 // UUID.
228 std::string device_address_path, uuid_path;
229 base::ReplaceChars(device_address_, ":-", "_", &device_address_path);
230 base::ReplaceChars(uuid_.canonical_value(), ":-", "_", &uuid_path);
231 if (!device_address_path.empty()) {
232 object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" +
233 device_address_path + "/" + uuid_path);
234 } else {
235 object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" +
236 uuid_path);
239 // Create the service provider for the profile object.
240 dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
241 profile_.reset(BluetoothProfileServiceProvider::Create(
242 system_bus, object_path_, this));
243 DCHECK(profile_.get());
245 // Before reaching out to the Bluetooth Daemon to register a listening socket,
246 // make sure it's actually running. If not, report success and carry on;
247 // the profile will be registered when the daemon becomes available.
248 if (adapter_ && !adapter_->IsPresent()) {
249 VLOG(1) << object_path_.value() << ": Delaying profile registration.";
250 success_callback.Run();
251 return;
254 VLOG(1) << object_path_.value() << ": Registering profile.";
255 DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
256 RegisterProfile(
257 object_path_,
258 uuid_.canonical_value(),
259 *options_,
260 base::Bind(&BluetoothSocketChromeOS::OnRegisterProfile,
261 this,
262 success_callback,
263 error_callback),
264 base::Bind(&BluetoothSocketChromeOS::OnRegisterProfileError,
265 this,
266 error_callback));
269 void BluetoothSocketChromeOS::OnRegisterProfile(
270 const base::Closure& success_callback,
271 const ErrorCompletionCallback& error_callback) {
272 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
273 if (!device_path_.value().empty()) {
274 VLOG(1) << object_path_.value() << ": Profile registered, connecting to "
275 << device_path_.value();
277 DBusThreadManager::Get()->GetBluetoothDeviceClient()->
278 ConnectProfile(
279 device_path_,
280 uuid_.canonical_value(),
281 base::Bind(
282 &BluetoothSocketChromeOS::OnConnectProfile,
283 this,
284 success_callback),
285 base::Bind(
286 &BluetoothSocketChromeOS::OnConnectProfileError,
287 this,
288 error_callback));
289 } else {
290 VLOG(1) << object_path_.value() << ": Profile registered.";
291 success_callback.Run();
295 void BluetoothSocketChromeOS::OnRegisterProfileError(
296 const ErrorCompletionCallback& error_callback,
297 const std::string& error_name,
298 const std::string& error_message) {
299 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
300 LOG(WARNING) << object_path_.value() << ": Failed to register profile: "
301 << error_name << ": " << error_message;
302 error_callback.Run(error_message);
305 void BluetoothSocketChromeOS::OnConnectProfile(
306 const base::Closure& success_callback) {
307 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
308 VLOG(1) << object_path_.value() << ": Profile connected.";
309 UnregisterProfile();
310 success_callback.Run();
313 void BluetoothSocketChromeOS::OnConnectProfileError(
314 const ErrorCompletionCallback& error_callback,
315 const std::string& error_name,
316 const std::string& error_message) {
317 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
318 LOG(WARNING) << object_path_.value() << ": Failed to connect profile: "
319 << error_name << ": " << error_message;
320 UnregisterProfile();
321 error_callback.Run(error_message);
324 void BluetoothSocketChromeOS::AdapterPresentChanged(BluetoothAdapter* adapter,
325 bool present) {
326 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
327 DCHECK(!object_path_.value().empty());
328 DCHECK(profile_.get());
330 if (!present)
331 return;
333 VLOG(1) << object_path_.value() << ": Re-register profile.";
334 DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
335 RegisterProfile(
336 object_path_,
337 uuid_.canonical_value(),
338 *options_,
339 base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfile,
340 this),
341 base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfileError,
342 this));
345 void BluetoothSocketChromeOS::OnInternalRegisterProfile() {
346 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
348 VLOG(1) << object_path_.value() << ": Profile re-registered";
351 void BluetoothSocketChromeOS::OnInternalRegisterProfileError(
352 const std::string& error_name,
353 const std::string& error_message) {
354 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
356 // It's okay if the profile already exists, it means we registered it on
357 // initialization.
358 if (error_name == bluetooth_profile_manager::kErrorAlreadyExists)
359 return;
361 LOG(WARNING) << object_path_.value() << ": Failed to re-register profile: "
362 << error_name << ": " << error_message;
365 void BluetoothSocketChromeOS::Released() {
366 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
367 VLOG(1) << object_path_.value() << ": Release";
370 void BluetoothSocketChromeOS::NewConnection(
371 const dbus::ObjectPath& device_path,
372 scoped_ptr<dbus::FileDescriptor> fd,
373 const BluetoothProfileServiceProvider::Delegate::Options& options,
374 const ConfirmationCallback& callback) {
375 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
376 VLOG(1) << object_path_.value() << ": New connection from device: "
377 << device_path.value();
379 if (!device_path_.value().empty()) {
380 DCHECK(device_path_ == device_path);
382 socket_thread()->task_runner()->PostTask(
383 FROM_HERE,
384 base::Bind(
385 &BluetoothSocketChromeOS::DoNewConnection,
386 this,
387 device_path_,
388 base::Passed(&fd),
389 options,
390 callback));
391 } else {
392 linked_ptr<ConnectionRequest> request(new ConnectionRequest());
393 request->device_path = device_path;
394 request->fd = fd.Pass();
395 request->options = options;
396 request->callback = callback;
398 connection_request_queue_.push(request);
399 VLOG(1) << object_path_.value() << ": Connection is now pending.";
400 if (accept_request_) {
401 AcceptConnectionRequest();
406 void BluetoothSocketChromeOS::RequestDisconnection(
407 const dbus::ObjectPath& device_path,
408 const ConfirmationCallback& callback) {
409 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
410 VLOG(1) << object_path_.value() << ": Request disconnection";
411 callback.Run(SUCCESS);
414 void BluetoothSocketChromeOS::Cancel() {
415 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
416 VLOG(1) << object_path_.value() << ": Cancel";
418 if (!connection_request_queue_.size())
419 return;
421 // If the front request is being accepted mark it as cancelled, otherwise
422 // just pop it from the queue.
423 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
424 if (!request->accepting) {
425 request->cancelled = true;
426 } else {
427 connection_request_queue_.pop();
431 void BluetoothSocketChromeOS::AcceptConnectionRequest() {
432 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
433 DCHECK(accept_request_.get());
434 DCHECK(connection_request_queue_.size() >= 1);
436 VLOG(1) << object_path_.value() << ": Accepting pending connection.";
438 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
439 request->accepting = true;
441 BluetoothDeviceChromeOS* device =
442 static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
443 GetDeviceWithPath(request->device_path);
444 DCHECK(device);
446 scoped_refptr<BluetoothSocketChromeOS> client_socket =
447 BluetoothSocketChromeOS::CreateBluetoothSocket(
448 ui_task_runner(), socket_thread());
450 client_socket->device_address_ = device->GetAddress();
451 client_socket->device_path_ = request->device_path;
452 client_socket->uuid_ = uuid_;
454 socket_thread()->task_runner()->PostTask(
455 FROM_HERE,
456 base::Bind(
457 &BluetoothSocketChromeOS::DoNewConnection,
458 client_socket,
459 request->device_path,
460 base::Passed(&request->fd),
461 request->options,
462 base::Bind(&BluetoothSocketChromeOS::OnNewConnection,
463 this,
464 client_socket,
465 request->callback)));
468 void BluetoothSocketChromeOS::DoNewConnection(
469 const dbus::ObjectPath& device_path,
470 scoped_ptr<dbus::FileDescriptor> fd,
471 const BluetoothProfileServiceProvider::Delegate::Options& options,
472 const ConfirmationCallback& callback) {
473 DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
474 base::ThreadRestrictions::AssertIOAllowed();
475 fd->CheckValidity();
477 VLOG(1) << object_path_.value() << ": Validity check complete.";
478 if (!fd->is_valid()) {
479 LOG(WARNING) << object_path_.value() << " :" << fd->value()
480 << ": Invalid file descriptor received from Bluetooth Daemon.";
481 ui_task_runner()->PostTask(FROM_HERE,
482 base::Bind(callback, REJECTED));;
483 return;
486 if (tcp_socket()) {
487 LOG(WARNING) << object_path_.value() << ": Already connected";
488 ui_task_runner()->PostTask(FROM_HERE,
489 base::Bind(callback, REJECTED));;
490 return;
493 ResetTCPSocket();
495 // Note: We don't have a meaningful |IPEndPoint|, but that is ok since the
496 // TCPSocket implementation does not actually require one.
497 int net_result = tcp_socket()->AdoptConnectedSocket(fd->value(),
498 net::IPEndPoint());
499 if (net_result != net::OK) {
500 LOG(WARNING) << object_path_.value() << ": Error adopting socket: "
501 << std::string(net::ErrorToString(net_result));
502 ui_task_runner()->PostTask(FROM_HERE,
503 base::Bind(callback, REJECTED));;
504 return;
507 VLOG(2) << object_path_.value() << ": Taking descriptor, confirming success.";
508 fd->TakeValue();
509 ui_task_runner()->PostTask(FROM_HERE,
510 base::Bind(callback, SUCCESS));;
513 void BluetoothSocketChromeOS::OnNewConnection(
514 scoped_refptr<BluetoothSocket> socket,
515 const ConfirmationCallback& callback,
516 Status status) {
517 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
518 DCHECK(accept_request_.get());
519 DCHECK(connection_request_queue_.size() >= 1);
521 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
522 if (status == SUCCESS && !request->cancelled) {
523 BluetoothDeviceChromeOS* device =
524 static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
525 GetDeviceWithPath(request->device_path);
526 DCHECK(device);
528 accept_request_->success_callback.Run(device, socket);
529 } else {
530 accept_request_->error_callback.Run(kAcceptFailed);
533 accept_request_.reset(NULL);
534 connection_request_queue_.pop();
536 callback.Run(status);
539 void BluetoothSocketChromeOS::DoCloseListening() {
540 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
542 if (accept_request_) {
543 accept_request_->error_callback.Run(
544 net::ErrorToString(net::ERR_CONNECTION_CLOSED));
545 accept_request_.reset(NULL);
548 while (connection_request_queue_.size() > 0) {
549 linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
550 request->callback.Run(REJECTED);
551 connection_request_queue_.pop();
555 void BluetoothSocketChromeOS::UnregisterProfile() {
556 DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
557 DCHECK(!object_path_.value().empty());
558 DCHECK(profile_.get());
560 VLOG(1) << object_path_.value() << ": Unregister profile";
561 DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
562 UnregisterProfile(
563 object_path_,
564 base::Bind(&BluetoothSocketChromeOS::OnUnregisterProfile,
565 this,
566 object_path_),
567 base::Bind(&BluetoothSocketChromeOS::OnUnregisterProfileError,
568 this,
569 object_path_));
571 profile_.reset();
572 object_path_ = dbus::ObjectPath("");
575 void BluetoothSocketChromeOS::OnUnregisterProfile(
576 const dbus::ObjectPath& object_path) {
577 VLOG(1) << object_path.value() << ": Profile unregistered";
580 void BluetoothSocketChromeOS::OnUnregisterProfileError(
581 const dbus::ObjectPath& object_path,
582 const std::string& error_name,
583 const std::string& error_message) {
584 // It's okay if the profile doesn't exist, it means we haven't registered it
585 // yet.
586 if (error_name == bluetooth_profile_manager::kErrorDoesNotExist)
587 return;
589 LOG(WARNING) << object_path_.value() << ": Failed to unregister profile: "
590 << error_name << ": " << error_message;
593 } // namespace chromeos