| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "device/bluetooth/floss/bluetooth_socket_floss.h" |
| |
| #include "base/task/sequenced_task_runner.h" |
| #include "device/bluetooth/floss/bluetooth_adapter_floss.h" |
| #include "device/bluetooth/floss/bluetooth_device_floss.h" |
| #include "device/bluetooth/floss/floss_dbus_client.h" |
| #include "device/bluetooth/floss/floss_dbus_manager.h" |
| #include "device/bluetooth/floss/floss_socket_manager.h" |
| #include "net/base/ip_endpoint.h" |
| #include "net/base/net_errors.h" |
| |
| namespace floss { |
| namespace { |
| const char kInvalidSocketType[] = "Invalid Socket Type"; |
| const char kInvalidUUID[] = "Invalid UUID"; |
| const char kSocketNotListening[] = "Socket is not listening"; |
| const char kSocketListenFailed[] = "Socket failed to listen"; |
| const char kSocketAcceptFailed[] = "Socket failed to accept"; |
| |
| const char kSocketFailedToConnect[] = "Socket failed to connect"; |
| const char kSocketInvalidFd[] = "Socket has invalid fd"; |
| const char kSocketAlreadyConnected[] = "Socket already connected"; |
| const char kSocketErrorAdopting[] = "Socket error during adoption"; |
| } // namespace |
| |
| // static |
| scoped_refptr<BluetoothSocketFloss> BluetoothSocketFloss::CreateBluetoothSocket( |
| scoped_refptr<base::SequencedTaskRunner> ui_task_runner, |
| scoped_refptr<device::BluetoothSocketThread> socket_thread) { |
| DCHECK(ui_task_runner->RunsTasksInCurrentSequence()); |
| |
| return base::WrapRefCounted( |
| new BluetoothSocketFloss(ui_task_runner, socket_thread)); |
| } |
| |
| BluetoothSocketFloss::AcceptRequest::AcceptRequest() = default; |
| BluetoothSocketFloss::AcceptRequest::~AcceptRequest() = default; |
| |
| BluetoothSocketFloss::BluetoothSocketFloss( |
| scoped_refptr<base::SequencedTaskRunner> ui_task_runner, |
| scoped_refptr<device::BluetoothSocketThread> socket_thread) |
| : BluetoothSocketNet(ui_task_runner, socket_thread) {} |
| |
| BluetoothSocketFloss::~BluetoothSocketFloss() = default; |
| |
| void BluetoothSocketFloss::Connect(BluetoothDeviceFloss* device, |
| const FlossSocketManager::Security security, |
| const device::BluetoothUUID& uuid, |
| base::OnceClosure success_callback, |
| ErrorCompletionCallback error_callback) { |
| DCHECK(ui_task_runner()->RunsTasksInCurrentSequence()); |
| DCHECK(device); |
| |
| if (!uuid.IsValid()) { |
| std::move(error_callback).Run(kInvalidUUID); |
| return; |
| } |
| |
| adapter_ = base::WrapRefCounted(device->GetAdapter()); |
| device_address_ = device->GetAddress(); |
| |
| FlossDBusManager::Get()->GetSocketManager()->ConnectUsingRfcomm( |
| device->AsFlossDeviceId(), uuid, security, |
| base::BindOnce(&BluetoothSocketFloss::CompleteConnect, |
| weak_ptr_factory_.GetWeakPtr(), |
| std::move(success_callback), std::move(error_callback))); |
| } |
| |
| void BluetoothSocketFloss::Listen( |
| scoped_refptr<device::BluetoothAdapter> adapter, |
| FlossSocketManager::SocketType socket_type, |
| const absl::optional<device::BluetoothUUID>& uuid, |
| const device::BluetoothAdapter::ServiceOptions& service_options, |
| base::OnceClosure success_callback, |
| ErrorCompletionCallback error_callback) { |
| DCHECK(ui_task_runner()->RunsTasksInCurrentSequence()); |
| |
| // TODO(abps) - Figure out whether we can accept channel/psm from options. |
| FlossSocketManager::Security security = |
| (service_options.require_authentication && |
| *service_options.require_authentication) |
| ? FlossSocketManager::Security::kSecure |
| : FlossSocketManager::Security::kInsecure; |
| |
| if (!uuid->IsValid()) { |
| std::move(error_callback).Run(kInvalidUUID); |
| return; |
| } |
| |
| if (socket_type != FlossSocketManager::SocketType::kL2cap && |
| socket_type != FlossSocketManager::SocketType::kRfcomm) { |
| std::move(error_callback).Run(kInvalidSocketType); |
| return; |
| } |
| |
| adapter_ = adapter; |
| |
| ResponseCallback<FlossDBusClient::BtifStatus> callback = base::BindOnce( |
| &BluetoothSocketFloss::CompleteListen, weak_ptr_factory_.GetWeakPtr(), |
| std::move(success_callback), std::move(error_callback)); |
| |
| FlossSocketManager::ConnectionStateChanged state_change = |
| base::BindRepeating(&BluetoothSocketFloss::DoConnectionStateChanged, |
| weak_ptr_factory_.GetWeakPtr()); |
| |
| FlossSocketManager::ConnectionAccepted accepted = |
| base::BindRepeating(&BluetoothSocketFloss::DoConnectionAccepted, |
| weak_ptr_factory_.GetWeakPtr()); |
| |
| if (socket_type == FlossSocketManager::SocketType::kL2cap) { |
| FlossDBusManager::Get()->GetSocketManager()->ListenUsingL2cap( |
| security, std::move(callback), state_change, accepted); |
| } else if (socket_type == FlossSocketManager::SocketType::kRfcomm) { |
| DCHECK(uuid); |
| std::string name; |
| if (service_options.name) { |
| name = *service_options.name; |
| } |
| |
| FlossDBusManager::Get()->GetSocketManager()->ListenUsingRfcomm( |
| name, *uuid, security, std::move(callback), state_change, accepted); |
| } |
| } |
| |
| void BluetoothSocketFloss::Disconnect(base::OnceClosure callback) { |
| DCHECK(ui_task_runner()->RunsTasksInCurrentSequence()); |
| |
| // Cancel any tasks pending on the socket thread. |
| socket_task_tracker_.TryCancelAll(); |
| |
| // If this is a connecting socket, simply close self. |
| if (connecting_socket_info_.is_valid()) { |
| BluetoothSocketNet::Disconnect(std::move(callback)); |
| |
| // Note: Adapter needs to be cleared here or BluetoothSocketNet will be |
| // holding a weak pointer and try to destroy a scoped_refptr in a the socket |
| // thread. |
| adapter_ = nullptr; |
| connecting_socket_info_.id = FlossSocketManager::kInvalidSocketId; |
| } |
| // Close the socket manager instance. |
| else if (listening_socket_info_) { |
| FlossDBusManager::Get()->GetSocketManager()->Close( |
| listening_socket_info_->id, |
| base::BindOnce(&BluetoothSocketFloss::CompleteClose, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback))); |
| listening_socket_info_ = absl::nullopt; |
| pending_accept_socket_.reset(); |
| } |
| |
| // If there is a pending accept, clear it. |
| if (accept_request_) { |
| std::move(accept_request_->error_callback) |
| .Run(net::ErrorToString(net::ERR_CONNECTION_CLOSED)); |
| accept_request_.reset(nullptr); |
| } |
| |
| // Just dropping the queue is sufficient. No need to send anything back |
| // because these are already accepted connections. Queue has no |clear| |
| // function so just assign a new empty queue. |
| connection_request_queue_ = {}; |
| } |
| |
| void BluetoothSocketFloss::Accept(AcceptCompletionCallback success_callback, |
| ErrorCompletionCallback error_callback) { |
| DCHECK(ui_task_runner()->RunsTasksInCurrentSequence()); |
| |
| if (!listening_socket_info_) { |
| std::move(error_callback).Run(kSocketNotListening); |
| return; |
| } |
| |
| // Accept is already pending. |
| if (accept_request_.get()) { |
| std::move(error_callback).Run(net::ErrorToString(net::ERR_IO_PENDING)); |
| return; |
| } |
| |
| accept_request_ = std::make_unique<AcceptRequest>(); |
| accept_request_->success_callback = std::move(success_callback); |
| accept_request_->error_callback = std::move(error_callback); |
| |
| // If there's any already ready connections, dispatch right away. |
| if (connection_request_queue_.size() >= 1) { |
| CompleteListeningConnect(); |
| } |
| |
| // If this socket is currently not accepting at the platform level, flip this |
| // to accepting. |
| if (!is_accepting_) { |
| FlossDBusManager::Get()->GetSocketManager()->Accept( |
| listening_socket_info_->id, absl::nullopt, |
| base::BindOnce(&BluetoothSocketFloss::CompleteAccept, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| } |
| |
| void BluetoothSocketFloss::DoConnectionStateChanged( |
| FlossSocketManager::ServerSocketState state, |
| FlossSocketManager::FlossListeningSocket socket, |
| FlossDBusClient::BtifStatus status) { |
| // If we don't already have socket info, store it. |
| if (!listening_socket_info_) { |
| listening_socket_info_ = socket; |
| } |
| |
| // Every time we get a socket state update, the socket gets reset to a not |
| // accepting state. Mark our internal state to match that. |
| is_accepting_ = false; |
| |
| // We also always want to be accepting and queue up connections here to be |
| // consumed when in the ready state. |
| if (state == FlossSocketManager::ServerSocketState::kReady && |
| status == FlossDBusClient::BtifStatus::kSuccess) { |
| FlossDBusManager::Get()->GetSocketManager()->Accept( |
| listening_socket_info_->id, absl::nullopt, |
| base::BindOnce(&BluetoothSocketFloss::CompleteAccept, |
| weak_ptr_factory_.GetWeakPtr())); |
| return; |
| } |
| |
| if (status != FlossDBusClient::BtifStatus::kSuccess && accept_request_) { |
| std::move(accept_request_->error_callback) |
| .Run(net::ErrorToString(net::ERR_CONNECTION_FAILED)); |
| accept_request_.reset(nullptr); |
| return; |
| } |
| } |
| |
| void BluetoothSocketFloss::DoConnectionAccepted( |
| FlossSocketManager::FlossSocket&& socket) { |
| // Queue dispatch of new connection. |
| connection_request_queue_.push(std::move(socket)); |
| |
| // If there's an accept ready, dispatch immediately. |
| if (accept_request_) { |
| CompleteListeningConnect(); |
| } |
| } |
| |
| void BluetoothSocketFloss::CompleteListen( |
| base::OnceClosure success_callback, |
| ErrorCompletionCallback error_callback, |
| DBusResult<FlossDBusClient::BtifStatus> result) { |
| if (!result.has_value()) { |
| std::move(error_callback).Run(result.error().ToString()); |
| return; |
| } |
| |
| if (*result != FlossDBusClient::BtifStatus::kSuccess) { |
| std::move(error_callback).Run(kSocketListenFailed); |
| return; |
| } |
| |
| std::move(success_callback).Run(); |
| } |
| |
| void BluetoothSocketFloss::CompleteConnect( |
| base::OnceClosure success_callback, |
| ErrorCompletionCallback error_callback, |
| FlossDBusClient::BtifStatus status, |
| absl::optional<FlossSocketManager::FlossSocket>&& socket) { |
| DCHECK(ui_task_runner()->RunsTasksInCurrentSequence()); |
| socket_task_tracker_.PostTask( |
| socket_thread()->task_runner().get(), FROM_HERE, |
| base::BindOnce(&BluetoothSocketFloss::CompleteConnectionInSocketThread, |
| this, std::move(success_callback), |
| std::move(error_callback), status, std::move(socket))); |
| } |
| |
| void BluetoothSocketFloss::CompleteConnectionInSocketThread( |
| base::OnceClosure success_callback, |
| ErrorCompletionCallback error_callback, |
| FlossDBusClient::BtifStatus status, |
| absl::optional<FlossSocketManager::FlossSocket>&& socket) { |
| DCHECK(socket_thread()->task_runner()->RunsTasksInCurrentSequence()); |
| |
| if (status != FlossDBusClient::BtifStatus::kSuccess || !socket) { |
| LOG(ERROR) << device_address_ << ": Socket failed connection: " |
| << static_cast<uint32_t>(status); |
| ui_task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(error_callback), kSocketFailedToConnect)); |
| return; |
| } |
| |
| connecting_socket_info_ = std::move(*socket); |
| |
| if (!connecting_socket_info_.fd || !connecting_socket_info_.fd->is_valid()) { |
| LOG(WARNING) << device_address_ |
| << ": Invalid file descriptor received from Bluetooth daemon"; |
| ui_task_runner()->PostTask( |
| FROM_HERE, base::BindOnce(std::move(error_callback), kSocketInvalidFd)); |
| return; |
| } |
| |
| if (tcp_socket()) { |
| LOG(WARNING) << device_address_ << ": Already connected"; |
| ui_task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(error_callback), kSocketAlreadyConnected)); |
| return; |
| } |
| |
| ResetTCPSocket(); |
| |
| int net_result = tcp_socket()->AdoptConnectedSocket( |
| connecting_socket_info_.fd->release(), net::IPEndPoint()); |
| |
| if (net_result != net::OK) { |
| LOG(WARNING) << device_address_ << ": Error adopting socket: " |
| << std::string(net::ErrorToString(net_result)); |
| ui_task_runner()->PostTask( |
| FROM_HERE, |
| base::BindOnce(std::move(error_callback), kSocketErrorAdopting)); |
| return; |
| } |
| |
| ui_task_runner()->PostTask(FROM_HERE, std::move(success_callback)); |
| } |
| |
| void BluetoothSocketFloss::CompleteAccept( |
| DBusResult<FlossDBusClient::BtifStatus> result) { |
| if (!result.has_value() && accept_request_) { |
| std::move(accept_request_->error_callback).Run(result.error().ToString()); |
| accept_request_.reset(nullptr); |
| return; |
| } else if (!result.has_value()) { |
| return; |
| } |
| |
| if (*result != FlossDBusClient::BtifStatus::kSuccess && accept_request_) { |
| LOG(WARNING) << "Failed to listen on socket with uuid " |
| << listening_socket_info_->uuid.value(); |
| std::move(accept_request_->error_callback).Run(kSocketAcceptFailed); |
| accept_request_.reset(nullptr); |
| return; |
| } |
| |
| if (*result == FlossDBusClient::BtifStatus::kSuccess) { |
| is_accepting_ = true; |
| } |
| } |
| |
| void BluetoothSocketFloss::CompleteClose( |
| base::OnceClosure callback, |
| DBusResult<FlossDBusClient::BtifStatus> result) { |
| if (result.has_value()) { |
| DVLOG(1) << "Result of closing socket = " << static_cast<uint32_t>(*result); |
| } |
| |
| is_accepting_ = false; |
| std::move(callback).Run(); |
| } |
| |
| void BluetoothSocketFloss::CompleteListeningConnect() { |
| DCHECK(ui_task_runner()->RunsTasksInCurrentSequence()); |
| |
| if (!listening_socket_info_) { |
| LOG(ERROR) << "Tried to complete connect on socket that isn't listening."; |
| return; |
| } |
| |
| if (connection_request_queue_.size() == 0) { |
| LOG(ERROR) << "Tried to complete connect with no pending connections."; |
| return; |
| } |
| |
| if (!accept_request_) { |
| LOG(ERROR) << "Tried to complete connect with no pending accept."; |
| return; |
| } |
| |
| pending_accept_socket_ = BluetoothSocketFloss::CreateBluetoothSocket( |
| ui_task_runner(), socket_thread()); |
| |
| absl::optional<FlossSocketManager::FlossSocket> sock( |
| std::move(connection_request_queue_.front())); |
| connection_request_queue_.pop(); |
| |
| device::BluetoothDevice* device = |
| adapter_->GetDevice(sock->remote_device.address); |
| |
| socket_task_tracker_.PostTask( |
| socket_thread()->task_runner().get(), FROM_HERE, |
| base::BindOnce( |
| &BluetoothSocketFloss::CompleteConnectionInSocketThread, |
| pending_accept_socket_.get(), |
| base::BindOnce(std::move(accept_request_->success_callback), device, |
| pending_accept_socket_), |
| std::move(accept_request_->error_callback), |
| FlossDBusClient::BtifStatus::kSuccess, std::move(sock))); |
| |
| // The last accept request has been consumed by the socket above. |
| accept_request_.reset(nullptr); |
| } |
| |
| } // namespace floss |