Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / device / bluetooth / bluetooth_socket_chromeos.cc
index 86cdc48..e401a3d 100644 (file)
@@ -4,22 +4,49 @@
 
 #include "device/bluetooth/bluetooth_socket_chromeos.h"
 
+#include <queue>
 #include <string>
 
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback.h"
 #include "base/logging.h"
+#include "base/memory/linked_ptr.h"
 #include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
 #include "base/sequenced_task_runner.h"
+#include "base/strings/string_util.h"
+#include "base/task_runner_util.h"
 #include "base/threading/thread_restrictions.h"
+#include "base/threading/worker_pool.h"
+#include "chromeos/dbus/bluetooth_device_client.h"
+#include "chromeos/dbus/bluetooth_profile_manager_client.h"
+#include "chromeos/dbus/bluetooth_profile_service_provider.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "dbus/bus.h"
 #include "dbus/file_descriptor.h"
+#include "dbus/object_path.h"
+#include "device/bluetooth/bluetooth_adapter.h"
+#include "device/bluetooth/bluetooth_adapter_chromeos.h"
+#include "device/bluetooth/bluetooth_device.h"
+#include "device/bluetooth/bluetooth_device_chromeos.h"
 #include "device/bluetooth/bluetooth_socket.h"
 #include "device/bluetooth/bluetooth_socket_net.h"
 #include "device/bluetooth/bluetooth_socket_thread.h"
 #include "net/base/ip_endpoint.h"
 #include "net/base/net_errors.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+using device::BluetoothAdapter;
+using device::BluetoothDevice;
+using device::BluetoothSocketThread;
+using device::BluetoothUUID;
 
 namespace {
 
-const char kSocketAlreadyConnected[] = "Socket is already connected.";
+const char kAcceptFailed[] = "Failed to accept connection.";
+const char kInvalidUUID[] = "Invalid UUID";
+const char kSocketNotListening[] = "Socket is not listening.";
 
 }  // namespace
 
@@ -29,57 +56,437 @@ namespace chromeos {
 scoped_refptr<BluetoothSocketChromeOS>
 BluetoothSocketChromeOS::CreateBluetoothSocket(
     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-    scoped_refptr<device::BluetoothSocketThread> socket_thread,
-    net::NetLog* net_log,
-    const net::NetLog::Source& source) {
+    scoped_refptr<BluetoothSocketThread> socket_thread) {
   DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
 
   return make_scoped_refptr(
-      new BluetoothSocketChromeOS(
-          ui_task_runner, socket_thread, net_log, source));
+      new BluetoothSocketChromeOS(ui_task_runner, socket_thread));
 }
 
+BluetoothSocketChromeOS::AcceptRequest::AcceptRequest() {}
+
+BluetoothSocketChromeOS::AcceptRequest::~AcceptRequest() {}
+
+BluetoothSocketChromeOS::ConnectionRequest::ConnectionRequest()
+    : accepting(false),
+      cancelled(false) {}
+
+BluetoothSocketChromeOS::ConnectionRequest::~ConnectionRequest() {}
+
 BluetoothSocketChromeOS::BluetoothSocketChromeOS(
     scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
-    scoped_refptr<device::BluetoothSocketThread> socket_thread,
-    net::NetLog* net_log,
-    const net::NetLog::Source& source)
-    : BluetoothSocketNet(ui_task_runner, socket_thread, net_log, source) {
+    scoped_refptr<BluetoothSocketThread> socket_thread)
+    : BluetoothSocketNet(ui_task_runner, socket_thread) {
 }
 
 BluetoothSocketChromeOS::~BluetoothSocketChromeOS() {
+  DCHECK(object_path_.value().empty());
+  DCHECK(profile_.get() == NULL);
+
+  if (adapter_.get()) {
+    adapter_->RemoveObserver(this);
+    adapter_ = NULL;
+  }
 }
 
 void BluetoothSocketChromeOS::Connect(
-    scoped_ptr<dbus::FileDescriptor> fd,
+    const BluetoothDeviceChromeOS* device,
+    const BluetoothUUID& uuid,
+    SecurityLevel security_level,
+    const base::Closure& success_callback,
+    const ErrorCompletionCallback& error_callback) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  DCHECK(object_path_.value().empty());
+  DCHECK(!profile_.get());
+
+  if (!uuid.IsValid()) {
+    error_callback.Run(kInvalidUUID);
+    return;
+  }
+
+  device_address_ = device->GetAddress();
+  device_path_ = device->object_path();
+  uuid_ = uuid;
+  options_.reset(new BluetoothProfileManagerClient::Options());
+  if (security_level == SECURITY_LEVEL_LOW)
+    options_->require_authentication.reset(new bool(false));
+
+  RegisterProfile(success_callback, error_callback);
+}
+
+void BluetoothSocketChromeOS::Listen(
+    scoped_refptr<BluetoothAdapter> adapter,
+    SocketType socket_type,
+    const BluetoothUUID& uuid,
+    const BluetoothAdapter::ServiceOptions& service_options,
+    const base::Closure& success_callback,
+    const ErrorCompletionCallback& error_callback) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  DCHECK(object_path_.value().empty());
+  DCHECK(!profile_.get());
+
+  if (!uuid.IsValid()) {
+    error_callback.Run(kInvalidUUID);
+    return;
+  }
+
+  adapter_ = adapter;
+  adapter_->AddObserver(this);
+
+  uuid_ = uuid;
+  options_.reset(new BluetoothProfileManagerClient::Options());
+  if (service_options.name)
+    options_->name.reset(new std::string(*service_options.name));
+
+  switch (socket_type) {
+    case kRfcomm:
+      options_->channel.reset(
+          new uint16(service_options.channel ? *service_options.channel : 0));
+      break;
+    case kL2cap:
+      options_->psm.reset(
+          new uint16(service_options.psm ? *service_options.psm : 0));
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  RegisterProfile(success_callback, error_callback);
+}
+
+void BluetoothSocketChromeOS::Close() {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+  if (profile_)
+    UnregisterProfile();
+
+  // In the case below, where an asynchronous task gets posted on the socket
+  // thread in BluetoothSocketNet::Close, a reference will be held to this
+  // socket by the callback. This may cause the BluetoothAdapter to outlive
+  // DBusThreadManager during shutdown if that callback executes too late.
+  if (adapter_.get()) {
+    adapter_->RemoveObserver(this);
+    adapter_ = NULL;
+  }
+
+  if (!device_path_.value().empty()) {
+    BluetoothSocketNet::Close();
+  } else {
+    DoCloseListening();
+  }
+}
+
+void BluetoothSocketChromeOS::Disconnect(const base::Closure& callback) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+  if (profile_)
+   UnregisterProfile();
+
+  if (!device_path_.value().empty()) {
+    BluetoothSocketNet::Disconnect(callback);
+  } else {
+    DoCloseListening();
+    callback.Run();
+  }
+}
+
+void BluetoothSocketChromeOS::Accept(
+    const AcceptCompletionCallback& success_callback,
+    const ErrorCompletionCallback& error_callback) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+  if (!device_path_.value().empty()) {
+    error_callback.Run(kSocketNotListening);
+    return;
+  }
+
+  // Only one pending accept at a time
+  if (accept_request_.get()) {
+    error_callback.Run(net::ErrorToString(net::ERR_IO_PENDING));
+    return;
+  }
+
+  accept_request_.reset(new AcceptRequest);
+  accept_request_->success_callback = success_callback;
+  accept_request_->error_callback = error_callback;
+
+  if (connection_request_queue_.size() >= 1) {
+    AcceptConnectionRequest();
+  }
+}
+
+void BluetoothSocketChromeOS::RegisterProfile(
     const base::Closure& success_callback,
     const ErrorCompletionCallback& error_callback) {
   DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  DCHECK(object_path_.value().empty());
+  DCHECK(!profile_.get());
+
+  // The object path is relatively meaningless, but has to be unique, so for
+  // connecting profiles use a combination of the device address and profile
+  // UUID.
+  std::string device_address_path, uuid_path;
+  base::ReplaceChars(device_address_, ":-", "_", &device_address_path);
+  base::ReplaceChars(uuid_.canonical_value(), ":-", "_", &uuid_path);
+  if (!device_address_path.empty()) {
+    object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" +
+                                    device_address_path + "/" + uuid_path);
+  } else {
+    object_path_ = dbus::ObjectPath("/org/chromium/bluetooth_profile/" +
+                                    uuid_path);
+  }
+
+  // Create the service provider for the profile object.
+  dbus::Bus* system_bus = DBusThreadManager::Get()->GetSystemBus();
+  profile_.reset(BluetoothProfileServiceProvider::Create(
+      system_bus, object_path_, this));
+  DCHECK(profile_.get());
+
+  // Before reaching out to the Bluetooth Daemon to register a listening socket,
+  // make sure it's actually running. If not, report success and carry on;
+  // the profile will be registered when the daemon becomes available.
+  if (adapter_.get() && !adapter_->IsPresent()) {
+    VLOG(1) << object_path_.value() << ": Delaying profile registration.";
+    success_callback.Run();
+    return;
+  }
+
+  VLOG(1) << object_path_.value() << ": Registering profile.";
+  DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
+      RegisterProfile(
+          object_path_,
+          uuid_.canonical_value(),
+          *options_,
+          base::Bind(&BluetoothSocketChromeOS::OnRegisterProfile,
+                     this,
+                     success_callback,
+                     error_callback),
+          base::Bind(&BluetoothSocketChromeOS::OnRegisterProfileError,
+                     this,
+                     error_callback));
+}
+
+void BluetoothSocketChromeOS::OnRegisterProfile(
+    const base::Closure& success_callback,
+    const ErrorCompletionCallback& error_callback) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  if (!device_path_.value().empty()) {
+    VLOG(1) << object_path_.value() << ": Profile registered, connecting to "
+            << device_path_.value();
+
+    DBusThreadManager::Get()->GetBluetoothDeviceClient()->
+        ConnectProfile(
+            device_path_,
+            uuid_.canonical_value(),
+            base::Bind(
+                &BluetoothSocketChromeOS::OnConnectProfile,
+                this,
+                success_callback),
+            base::Bind(
+                &BluetoothSocketChromeOS::OnConnectProfileError,
+                this,
+                error_callback));
+  } else {
+    VLOG(1) << object_path_.value() << ": Profile registered.";
+    success_callback.Run();
+  }
+}
+
+void BluetoothSocketChromeOS::OnRegisterProfileError(
+    const ErrorCompletionCallback& error_callback,
+    const std::string& error_name,
+    const std::string& error_message) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  LOG(WARNING) << object_path_.value() << ": Failed to register profile: "
+               << error_name << ": " << error_message;
+  error_callback.Run(error_message);
+}
+
+void BluetoothSocketChromeOS::OnConnectProfile(
+    const base::Closure& success_callback) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  VLOG(1) << object_path_.value() << ": Profile connected.";
+  UnregisterProfile();
+  success_callback.Run();
+}
+
+void BluetoothSocketChromeOS::OnConnectProfileError(
+    const ErrorCompletionCallback& error_callback,
+    const std::string& error_name,
+    const std::string& error_message) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  LOG(WARNING) << object_path_.value() << ": Failed to connect profile: "
+               << error_name << ": " << error_message;
+  UnregisterProfile();
+  error_callback.Run(error_message);
+}
+
+void BluetoothSocketChromeOS::AdapterPresentChanged(BluetoothAdapter* adapter,
+                                                    bool present) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  DCHECK(!object_path_.value().empty());
+  DCHECK(profile_.get());
+
+  if (!present)
+    return;
+
+  VLOG(1) << object_path_.value() << ": Re-register profile.";
+  DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
+      RegisterProfile(
+          object_path_,
+          uuid_.canonical_value(),
+          *options_,
+          base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfile,
+                     this),
+          base::Bind(&BluetoothSocketChromeOS::OnInternalRegisterProfileError,
+                     this));
+}
+
+void BluetoothSocketChromeOS::OnInternalRegisterProfile() {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+  VLOG(1) << object_path_.value() << ": Profile re-registered";
+}
+
+void BluetoothSocketChromeOS::OnInternalRegisterProfileError(
+    const std::string& error_name,
+    const std::string& error_message) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+  // It's okay if the profile already exists, it means we registered it on
+  // initialization.
+  if (error_name == bluetooth_profile_manager::kErrorAlreadyExists)
+    return;
+
+  LOG(WARNING) << object_path_.value() << ": Failed to re-register profile: "
+               << error_name << ": " << error_message;
+}
+
+void BluetoothSocketChromeOS::Released() {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  VLOG(1) << object_path_.value() << ": Release";
+}
+
+void BluetoothSocketChromeOS::NewConnection(
+    const dbus::ObjectPath& device_path,
+    scoped_ptr<dbus::FileDescriptor> fd,
+    const BluetoothProfileServiceProvider::Delegate::Options& options,
+    const ConfirmationCallback& callback) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  VLOG(1) << object_path_.value() << ": New connection from device: "
+          << device_path.value();
+
+  if (!device_path_.value().empty()) {
+    DCHECK(device_path_ == device_path);
+
+    socket_thread()->task_runner()->PostTask(
+        FROM_HERE,
+        base::Bind(
+            &BluetoothSocketChromeOS::DoNewConnection,
+            this,
+            device_path_,
+            base::Passed(&fd),
+            options,
+            callback));
+  } else {
+    linked_ptr<ConnectionRequest> request(new ConnectionRequest());
+    request->device_path = device_path;
+    request->fd = fd.Pass();
+    request->options = options;
+    request->callback = callback;
+
+    connection_request_queue_.push(request);
+    VLOG(1) << object_path_.value() << ": Connection is now pending.";
+    if (accept_request_) {
+      AcceptConnectionRequest();
+    }
+  }
+}
+
+void BluetoothSocketChromeOS::RequestDisconnection(
+    const dbus::ObjectPath& device_path,
+    const ConfirmationCallback& callback) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  VLOG(1) << object_path_.value() << ": Request disconnection";
+  callback.Run(SUCCESS);
+}
+
+void BluetoothSocketChromeOS::Cancel() {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  VLOG(1) << object_path_.value() << ": Cancel";
+
+  if (!connection_request_queue_.size())
+    return;
+
+  // If the front request is being accepted mark it as cancelled, otherwise
+  // just pop it from the queue.
+  linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
+  if (!request->accepting) {
+    request->cancelled = true;
+  } else {
+    connection_request_queue_.pop();
+  }
+}
+
+void BluetoothSocketChromeOS::AcceptConnectionRequest() {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  DCHECK(accept_request_.get());
+  DCHECK(connection_request_queue_.size() >= 1);
+
+  VLOG(1) << object_path_.value() << ": Accepting pending connection.";
+
+  linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
+  request->accepting = true;
+
+  BluetoothDeviceChromeOS* device =
+      static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
+          GetDeviceWithPath(request->device_path);
+  DCHECK(device);
+
+  scoped_refptr<BluetoothSocketChromeOS> client_socket =
+      BluetoothSocketChromeOS::CreateBluetoothSocket(
+          ui_task_runner(), socket_thread());
+
+  client_socket->device_address_ = device->GetAddress();
+  client_socket->device_path_ = request->device_path;
+  client_socket->uuid_ = uuid_;
 
   socket_thread()->task_runner()->PostTask(
       FROM_HERE,
       base::Bind(
-          &BluetoothSocketChromeOS::DoConnect,
-          this,
-          base::Passed(&fd),
-          base::Bind(&BluetoothSocketChromeOS::PostSuccess,
+          &BluetoothSocketChromeOS::DoNewConnection,
+          client_socket,
+          request->device_path,
+          base::Passed(&request->fd),
+          request->options,
+          base::Bind(&BluetoothSocketChromeOS::OnNewConnection,
                      this,
-                     success_callback),
-          base::Bind(&BluetoothSocketChromeOS::PostErrorCompletion,
-                     this,
-                     error_callback)));
+                     client_socket,
+                     request->callback)));
 }
 
-void BluetoothSocketChromeOS::DoConnect(
+void BluetoothSocketChromeOS::DoNewConnection(
+    const dbus::ObjectPath& device_path,
     scoped_ptr<dbus::FileDescriptor> fd,
-    const base::Closure& success_callback,
-    const ErrorCompletionCallback& error_callback) {
+    const BluetoothProfileServiceProvider::Delegate::Options& options,
+    const ConfirmationCallback& callback) {
   DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
   base::ThreadRestrictions::AssertIOAllowed();
-  DCHECK(fd->is_valid());
+  fd->CheckValidity();
+
+  VLOG(1) << object_path_.value() << ": Validity check complete.";
+  if (!fd->is_valid()) {
+    LOG(WARNING) << object_path_.value() << " :" << fd->value()
+                 << ": Invalid file descriptor received from Bluetooth Daemon.";
+    ui_task_runner()->PostTask(FROM_HERE,
+                               base::Bind(callback, REJECTED));;
+    return;
+  }
 
   if (tcp_socket()) {
-    error_callback.Run(kSocketAlreadyConnected);
+    LOG(WARNING) << object_path_.value() << ": Already connected";
+    ui_task_runner()->PostTask(FROM_HERE,
+                               base::Bind(callback, REJECTED));;
     return;
   }
 
@@ -90,13 +497,97 @@ void BluetoothSocketChromeOS::DoConnect(
   int net_result = tcp_socket()->AdoptConnectedSocket(fd->value(),
                                                       net::IPEndPoint());
   if (net_result != net::OK) {
-    error_callback.Run("Error connecting to socket: " +
-                       std::string(net::ErrorToString(net_result)));
+    LOG(WARNING) << object_path_.value() << ": Error adopting socket: "
+                 << std::string(net::ErrorToString(net_result));
+    ui_task_runner()->PostTask(FROM_HERE,
+                               base::Bind(callback, REJECTED));;
     return;
   }
 
+  VLOG(2) << object_path_.value() << ": Taking descriptor, confirming success.";
   fd->TakeValue();
-  success_callback.Run();
+  ui_task_runner()->PostTask(FROM_HERE,
+                             base::Bind(callback, SUCCESS));;
+}
+
+void BluetoothSocketChromeOS::OnNewConnection(
+    scoped_refptr<BluetoothSocket> socket,
+    const ConfirmationCallback& callback,
+    Status status) {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  DCHECK(accept_request_.get());
+  DCHECK(connection_request_queue_.size() >= 1);
+
+  linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
+  if (status == SUCCESS && !request->cancelled) {
+    BluetoothDeviceChromeOS* device =
+        static_cast<BluetoothAdapterChromeOS*>(adapter_.get())->
+            GetDeviceWithPath(request->device_path);
+    DCHECK(device);
+
+    accept_request_->success_callback.Run(device, socket);
+  } else {
+    accept_request_->error_callback.Run(kAcceptFailed);
+  }
+
+  accept_request_.reset(NULL);
+  connection_request_queue_.pop();
+
+  callback.Run(status);
+}
+
+void BluetoothSocketChromeOS::DoCloseListening() {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+
+  if (accept_request_) {
+    accept_request_->error_callback.Run(
+        net::ErrorToString(net::ERR_CONNECTION_CLOSED));
+    accept_request_.reset(NULL);
+  }
+
+  while (connection_request_queue_.size() > 0) {
+    linked_ptr<ConnectionRequest> request = connection_request_queue_.front();
+    request->callback.Run(REJECTED);
+    connection_request_queue_.pop();
+  }
+}
+
+void BluetoothSocketChromeOS::UnregisterProfile() {
+  DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
+  DCHECK(!object_path_.value().empty());
+  DCHECK(profile_.get());
+
+  VLOG(1) << object_path_.value() << ": Unregister profile";
+  DBusThreadManager::Get()->GetBluetoothProfileManagerClient()->
+      UnregisterProfile(
+          object_path_,
+          base::Bind(&BluetoothSocketChromeOS::OnUnregisterProfile,
+                     this,
+                     object_path_),
+          base::Bind(&BluetoothSocketChromeOS::OnUnregisterProfileError,
+                     this,
+                     object_path_));
+
+  profile_.reset();
+  object_path_ = dbus::ObjectPath("");
+}
+
+void BluetoothSocketChromeOS::OnUnregisterProfile(
+    const dbus::ObjectPath& object_path) {
+  VLOG(1) << object_path.value() << ": Profile unregistered";
+}
+
+void BluetoothSocketChromeOS::OnUnregisterProfileError(
+    const dbus::ObjectPath& object_path,
+    const std::string& error_name,
+    const std::string& error_message) {
+  // It's okay if the profile doesn't exist, it means we haven't registered it
+  // yet.
+  if (error_name == bluetooth_profile_manager::kErrorDoesNotExist)
+    return;
+
+  LOG(WARNING) << object_path_.value() << ": Failed to unregister profile: "
+               << error_name << ": " << error_message;
 }
 
 }  // namespace chromeos