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
40using device::BluetoothAdapter;
41using device::BluetoothDevice;
42using device::BluetoothSocketThread;
43using device::BluetoothUUID;
44
45namespace {
46
47const char kAcceptFailed[] = "Failed to accept connection.";
48const char kInvalidUUID[] = "Invalid UUID";
49const char kSocketNotListening[] = "Socket is not listening.";
50
51}  // namespace
52
53namespace chromeos {
54
55// static
56scoped_refptr<BluetoothSocketChromeOS>
57BluetoothSocketChromeOS::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
66BluetoothSocketChromeOS::AcceptRequest::AcceptRequest() {}
67
68BluetoothSocketChromeOS::AcceptRequest::~AcceptRequest() {}
69
70BluetoothSocketChromeOS::ConnectionRequest::ConnectionRequest()
71    : accepting(false),
72      cancelled(false) {}
73
74BluetoothSocketChromeOS::ConnectionRequest::~ConnectionRequest() {}
75
76BluetoothSocketChromeOS::BluetoothSocketChromeOS(
77    scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
78    scoped_refptr<BluetoothSocketThread> socket_thread)
79    : BluetoothSocketNet(ui_task_runner, socket_thread) {
80}
81
82BluetoothSocketChromeOS::~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
92void 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
117void 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
157void 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
179void 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
193void 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
218void 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
269void 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
295void 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
305void 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
313void 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
324void 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
345void BluetoothSocketChromeOS::OnInternalRegisterProfile() {
346  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
347
348  VLOG(1) << object_path_.value() << ": Profile re-registered";
349}
350
351void 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
365void BluetoothSocketChromeOS::Released() {
366  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
367  VLOG(1) << object_path_.value() << ": Release";
368}
369
370void 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
406void 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
414void 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
431void 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
468void 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
513void 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
539void 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
555void 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
575void BluetoothSocketChromeOS::OnUnregisterProfile(
576    const dbus::ObjectPath& object_path) {
577  VLOG(1) << object_path.value() << ": Profile unregistered";
578}
579
580void 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
594