Index: device/bluetooth/bluetooth_socket_chromeos.cc |
diff --git a/device/bluetooth/bluetooth_socket_chromeos.cc b/device/bluetooth/bluetooth_socket_chromeos.cc |
index c281a595100d29a1905ed956431ddaccae168dd7..bf0efa0eb82224e6d8c2cf416730681ed647e6a5 100644 |
--- a/device/bluetooth/bluetooth_socket_chromeos.cc |
+++ b/device/bluetooth/bluetooth_socket_chromeos.cc |
@@ -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,7 +56,7 @@ namespace chromeos { |
scoped_refptr<BluetoothSocketChromeOS> |
BluetoothSocketChromeOS::CreateBluetoothSocket( |
scoped_refptr<base::SequencedTaskRunner> ui_task_runner, |
- scoped_refptr<device::BluetoothSocketThread> socket_thread, |
+ scoped_refptr<BluetoothSocketThread> socket_thread, |
net::NetLog* net_log, |
const net::NetLog::Source& source) { |
DCHECK(ui_task_runner->RunsTasksOnCurrentThread()); |
@@ -39,53 +66,425 @@ BluetoothSocketChromeOS::CreateBluetoothSocket( |
ui_task_runner, socket_thread, net_log, source)); |
} |
+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, |
+ scoped_refptr<BluetoothSocketThread> socket_thread, |
net::NetLog* net_log, |
const net::NetLog::Source& source) |
: BluetoothSocketNet(ui_task_runner, socket_thread, net_log, source) { |
} |
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, |
const base::Closure& success_callback, |
const ErrorCompletionCallback& error_callback) { |
DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); |
+ DCHECK(object_path_.value().empty()); |
+ DCHECK(!profile_.get()); |
- socket_thread()->task_runner()->PostTask( |
- FROM_HERE, |
- base::Bind( |
- &BluetoothSocketChromeOS::DoConnect, |
- this, |
- base::Passed(&fd), |
- base::Bind(&BluetoothSocketChromeOS::PostSuccess, |
- this, |
- success_callback), |
- base::Bind(&BluetoothSocketChromeOS::PostErrorCompletion, |
- this, |
- error_callback))); |
+ if (!uuid.IsValid()) { |
+ error_callback.Run(kInvalidUUID); |
+ return; |
+ } |
+ |
+ device_address_ = device->GetAddress(); |
+ device_path_ = device->object_path(); |
+ uuid_ = uuid; |
+ options_.reset(new BluetoothProfileManagerClient::Options()); |
+ |
+ RegisterProfile(success_callback, error_callback); |
+} |
+ |
+void BluetoothSocketChromeOS::Listen( |
+ scoped_refptr<BluetoothAdapter> adapter, |
+ SocketType socket_type, |
+ const BluetoothUUID& uuid, |
+ int psm_or_channel, |
+ bool insecure, |
+ 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()); |
+ |
+ switch (socket_type) { |
+ case kRfcomm: |
+ options_->channel.reset(new uint16( |
+ psm_or_channel == BluetoothAdapter::kChannelAuto |
+ ? 0 : psm_or_channel)); |
+ break; |
+ case kL2cap: |
+ options_->psm.reset(new uint16( |
+ psm_or_channel == BluetoothAdapter::kPsmAuto |
+ ? 0 : psm_or_channel)); |
+ break; |
+ default: |
+ NOTREACHED(); |
+ } |
+ |
+ if (insecure) |
+ options_->require_authentication.reset(new bool(false)); |
+ |
+ RegisterProfile(success_callback, error_callback); |
+} |
+ |
+void BluetoothSocketChromeOS::Close() { |
+ DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); |
+ |
+ if (profile_) |
+ UnregisterProfile(); |
+ |
+ 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) { |
- NOTIMPLEMENTED(); |
+ 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::DoConnect( |
- scoped_ptr<dbus::FileDescriptor> fd, |
+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_ && !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(), |
+ net_log(), |
+ source()); |
+ |
+ 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::DoNewConnection, |
+ client_socket, |
+ request->device_path, |
+ base::Passed(&request->fd), |
+ request->options, |
+ base::Bind(&BluetoothSocketChromeOS::OnNewConnection, |
+ this, |
+ client_socket, |
+ request->callback))); |
+} |
+ |
+void BluetoothSocketChromeOS::DoNewConnection( |
+ const dbus::ObjectPath& device_path, |
+ scoped_ptr<dbus::FileDescriptor> fd, |
+ 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()) { |
+ 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; |
} |
@@ -96,13 +495,96 @@ 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; |
} |
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 |