Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / device / bluetooth / bluetooth_socket_chromeos.cc
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>
57 BluetoothSocketChromeOS::CreateBluetoothSocket(
58     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
59     scoped_refptr<BluetoothSocketThread> socket_thread) {
60   DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
61
62   return make_scoped_refptr(
63       new BluetoothSocketChromeOS(ui_task_runner, socket_thread));
64 }
65
66 BluetoothSocketChromeOS::AcceptRequest::AcceptRequest() {}
67
68 BluetoothSocketChromeOS::AcceptRequest::~AcceptRequest() {}
69
70 BluetoothSocketChromeOS::ConnectionRequest::ConnectionRequest()
71     : accepting(false),
72       cancelled(false) {}
73
74 BluetoothSocketChromeOS::ConnectionRequest::~ConnectionRequest() {}
75
76 BluetoothSocketChromeOS::BluetoothSocketChromeOS(
77     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
78     scoped_refptr<BluetoothSocketThread> socket_thread)
79     : BluetoothSocketNet(ui_task_runner, socket_thread) {
80 }
81
82 BluetoothSocketChromeOS::~BluetoothSocketChromeOS() {
83   DCHECK(object_path_.value().empty());
84   DCHECK(profile_.get() == NULL);
85
86   if (adapter_.get()) {
87     adapter_->RemoveObserver(this);
88     adapter_ = NULL;
89   }
90 }
91
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());
101
102   if (!uuid.IsValid()) {
103     error_callback.Run(kInvalidUUID);
104     return;
105   }
106
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));
113
114   RegisterProfile(success_callback, error_callback);
115 }
116
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());
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 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(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();
152   }
153
154   RegisterProfile(success_callback, error_callback);
155 }
156
157 void BluetoothSocketChromeOS::Close() {
158   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
159
160   if (profile_)
161     UnregisterProfile();
162
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;
170   }
171
172   if (!device_path_.value().empty()) {
173     BluetoothSocketNet::Close();
174   } else {
175     DoCloseListening();
176   }
177 }
178
179 void BluetoothSocketChromeOS::Disconnect(const base::Closure& callback) {
180   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
181
182   if (profile_)
183    UnregisterProfile();
184
185   if (!device_path_.value().empty()) {
186     BluetoothSocketNet::Disconnect(callback);
187   } else {
188     DoCloseListening();
189     callback.Run();
190   }
191 }
192
193 void BluetoothSocketChromeOS::Accept(
194     const AcceptCompletionCallback& success_callback,
195     const ErrorCompletionCallback& error_callback) {
196   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
197
198   if (!device_path_.value().empty()) {
199     error_callback.Run(kSocketNotListening);
200     return;
201   }
202
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;
207   }
208
209   accept_request_.reset(new AcceptRequest);
210   accept_request_->success_callback = success_callback;
211   accept_request_->error_callback = error_callback;
212
213   if (connection_request_queue_.size() >= 1) {
214     AcceptConnectionRequest();
215   }
216 }
217
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());
224
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);
237   }
238
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());
244
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_.get() && !adapter_->IsPresent()) {
249     VLOG(1) << object_path_.value() << ": Delaying profile registration.";
250     success_callback.Run();
251     return;
252   }
253
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));
267 }
268
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();
276
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();
292   }
293 }
294
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);
303 }
304
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();
311 }
312
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);
322 }
323
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());
329
330   if (!present)
331     return;
332
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));
343 }
344
345 void BluetoothSocketChromeOS::OnInternalRegisterProfile() {
346   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
347
348   VLOG(1) << object_path_.value() << ": Profile re-registered";
349 }
350
351 void BluetoothSocketChromeOS::OnInternalRegisterProfileError(
352     const std::string& error_name,
353     const std::string& error_message) {
354   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
355
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;
360
361   LOG(WARNING) << object_path_.value() << ": Failed to re-register profile: "
362                << error_name << ": " << error_message;
363 }
364
365 void BluetoothSocketChromeOS::Released() {
366   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
367   VLOG(1) << object_path_.value() << ": Release";
368 }
369
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();
378
379   if (!device_path_.value().empty()) {
380     DCHECK(device_path_ == device_path);
381
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;
397
398     connection_request_queue_.push(request);
399     VLOG(1) << object_path_.value() << ": Connection is now pending.";
400     if (accept_request_) {
401       AcceptConnectionRequest();
402     }
403   }
404 }
405
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);
412 }
413
414 void BluetoothSocketChromeOS::Cancel() {
415   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
416   VLOG(1) << object_path_.value() << ": Cancel";
417
418   if (!connection_request_queue_.size())
419     return;
420
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();
428   }
429 }
430
431 void BluetoothSocketChromeOS::AcceptConnectionRequest() {
432   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
433   DCHECK(accept_request_.get());
434   DCHECK(connection_request_queue_.size() >= 1);
435
436   VLOG(1) << object_path_.value() << ": Accepting pending connection.";
437
438   linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
439   request->accepting = true;
440
441   BluetoothDeviceChromeOS* device =
442       static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
443           GetDeviceWithPath(request->device_path);
444   DCHECK(device);
445
446   scoped_refptr<BluetoothSocketChromeOS> client_socket =
447       BluetoothSocketChromeOS::CreateBluetoothSocket(
448           ui_task_runner(), socket_thread());
449
450   client_socket->device_address_ = device->GetAddress();
451   client_socket->device_path_ = request->device_path;
452   client_socket->uuid_ = uuid_;
453
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)));
466 }
467
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();
476
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;
484   }
485
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;
491   }
492
493   ResetTCPSocket();
494
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;
505   }
506
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));;
511 }
512
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);
520
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);
527
528     accept_request_->success_callback.Run(device, socket);
529   } else {
530     accept_request_->error_callback.Run(kAcceptFailed);
531   }
532
533   accept_request_.reset(NULL);
534   connection_request_queue_.pop();
535
536   callback.Run(status);
537 }
538
539 void BluetoothSocketChromeOS::DoCloseListening() {
540   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
541
542   if (accept_request_) {
543     accept_request_->error_callback.Run(
544         net::ErrorToString(net::ERR_CONNECTION_CLOSED));
545     accept_request_.reset(NULL);
546   }
547
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();
552   }
553 }
554
555 void BluetoothSocketChromeOS::UnregisterProfile() {
556   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
557   DCHECK(!object_path_.value().empty());
558   DCHECK(profile_.get());
559
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_));
570
571   profile_.reset();
572   object_path_ = dbus::ObjectPath("");
573 }
574
575 void BluetoothSocketChromeOS::OnUnregisterProfile(
576     const dbus::ObjectPath& object_path) {
577   VLOG(1) << object_path.value() << ": Profile unregistered";
578 }
579
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;
588
589   LOG(WARNING) << object_path_.value() << ": Failed to unregister profile: "
590                << error_name << ": " << error_message;
591 }
592
593 }  // namespace chromeos