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