#include "services/device/serial/bluetooth_serial_port_impl.h"
#include <limits.h>
#include <algorithm>
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/functional/callback_helpers.h"
#include "net/base/io_buffer.h"
#include "services/device/public/cpp/bluetooth/bluetooth_utils.h"
#include "services/device/public/cpp/serial/serial_switches.h"
namespace device {
void BluetoothSerialPortImpl::Open(
scoped_refptr<BluetoothAdapter> adapter,
const std::string& address,
const BluetoothUUID& service_class_id,
mojom::SerialConnectionOptionsPtr options,
mojo::PendingRemote<mojom::SerialPortClient> client,
mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher,
OpenCallback callback) {
DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kEnableBluetoothSerialPortProfileInSerialApi));
auto* port = new BluetoothSerialPortImpl(
std::move(adapter), address, std::move(options), std::move(client),
std::move(watcher));
port->OpenSocket(service_class_id, std::move(callback));
}
BluetoothSerialPortImpl::BluetoothSerialPortImpl(
scoped_refptr<BluetoothAdapter> adapter,
const std::string& address,
mojom::SerialConnectionOptionsPtr options,
mojo::PendingRemote<mojom::SerialPortClient> client,
mojo::PendingRemote<mojom::SerialPortConnectionWatcher> watcher)
: watcher_(std::move(watcher)),
client_(std::move(client)),
in_stream_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
out_stream_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL),
bluetooth_adapter_(std::move(adapter)),
address_(address),
options_(std::move(options)) {
if (watcher_.is_bound()) {
watcher_.set_disconnect_handler(
base::BindOnce([](BluetoothSerialPortImpl* self) { delete self; },
base::Unretained(this)));
}
}
BluetoothSerialPortImpl::~BluetoothSerialPortImpl() {
if (bluetooth_socket_)
bluetooth_socket_->Disconnect(base::DoNothing());
}
void BluetoothSerialPortImpl::OpenSocket(const BluetoothUUID& service_class_id,
OpenCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
BluetoothDevice* device = bluetooth_adapter_->GetDevice(address_);
if (!device) {
std::move(callback).Run(mojo::NullRemote());
delete this;
return;
}
auto split_callback = base::SplitOnceCallback(std::move(callback));
device->ConnectToService(
service_class_id,
base::BindOnce(&BluetoothSerialPortImpl::OnSocketConnected,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.first)),
base::BindOnce(&BluetoothSerialPortImpl::OnSocketConnectedError,
weak_ptr_factory_.GetWeakPtr(),
std::move(split_callback.second)));
}
void BluetoothSerialPortImpl::OnSocketConnected(
OpenCallback callback,
scoped_refptr<BluetoothSocket> socket) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(socket);
bluetooth_socket_ = std::move(socket);
mojo::PendingRemote<mojom::SerialPort> port =
receiver_.BindNewPipeAndPassRemote();
receiver_.set_disconnect_handler(
base::BindOnce([](BluetoothSerialPortImpl* self) { delete self; },
base::Unretained(this)));
std::move(callback).Run(std::move(port));
}
void BluetoothSerialPortImpl::OnSocketConnectedError(
OpenCallback callback,
const std::string& message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(callback).Run(mojo::NullRemote());
delete this;
}
void BluetoothSerialPortImpl::StartWriting(
mojo::ScopedDataPipeConsumerHandle consumer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (in_stream_) {
receiver_.ReportBadMessage("Data pipe consumer still open.");
return;
}
if (!bluetooth_socket_) {
receiver_.ReportBadMessage("No Bluetooth socket.");
return;
}
in_stream_watcher_.Cancel();
in_stream_ = std::move(consumer);
in_stream_watcher_.Watch(
in_stream_.get(),
MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
base::BindRepeating(&BluetoothSerialPortImpl::WriteToSocket,
weak_ptr_factory_.GetWeakPtr()));
if (!write_pending_)
in_stream_watcher_.ArmOrNotify();
}
void BluetoothSerialPortImpl::StartReading(
mojo::ScopedDataPipeProducerHandle producer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (out_stream_) {
receiver_.ReportBadMessage("Data pipe producer still open.");
return;
}
if (!bluetooth_socket_) {
receiver_.ReportBadMessage("No Bluetooth socket.");
return;
}
out_stream_watcher_.Cancel();
out_stream_ = std::move(producer);
out_stream_watcher_.Watch(
out_stream_.get(),
MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
base::BindRepeating(&BluetoothSerialPortImpl::ReadFromSocketAndWriteOut,
weak_ptr_factory_.GetWeakPtr()));
if (!read_pending_)
out_stream_watcher_.ArmOrNotify();
}
void BluetoothSerialPortImpl::ReadFromSocketAndWriteOut(
MojoResult result,
const mojo::HandleSignalsState& state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (result) {
case MOJO_RESULT_OK:
DCHECK(!read_pending_);
ReadMore();
break;
case MOJO_RESULT_SHOULD_WAIT:
out_stream_watcher_.ArmOrNotify();
break;
case MOJO_RESULT_FAILED_PRECONDITION:
case MOJO_RESULT_CANCELLED:
out_stream_watcher_.Cancel();
out_stream_.reset();
break;
default:
NOTREACHED() << "Unexpected Mojo result: " << result;
}
}
void BluetoothSerialPortImpl::ResetPendingWriteBuffer() {
pending_write_buffer_ = base::span<char>();
}
void BluetoothSerialPortImpl::ResetReceiveBuffer() {
receive_buffer_size_ = 0;
receive_buffer_next_byte_pos_ = 0;
receive_buffer_.reset();
}
void BluetoothSerialPortImpl::ReadMore() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(out_stream_.is_valid());
DCHECK(!read_pending_);
void* buffer = nullptr;
uint32_t buffer_num_bytes = 0;
MojoResult result = out_stream_->BeginWriteData(&buffer, &buffer_num_bytes,
MOJO_WRITE_DATA_FLAG_NONE);
if (result == MOJO_RESULT_SHOULD_WAIT) {
out_stream_watcher_.ArmOrNotify();
return;
}
if (result != MOJO_RESULT_OK) {
out_stream_watcher_.Cancel();
out_stream_.reset();
return;
}
if (!bluetooth_socket_) {
mojo::ReportBadMessage("No Bluetooth socket.");
return;
}
if (receive_buffer_) {
const size_t num_remaining_bytes =
receive_buffer_size_ - receive_buffer_next_byte_pos_;
const size_t bytes_to_copy =
std::min(num_remaining_bytes, size_t{buffer_num_bytes});
std::copy(
receive_buffer_->data() + receive_buffer_next_byte_pos_,
receive_buffer_->data() + receive_buffer_next_byte_pos_ + bytes_to_copy,
reinterpret_cast<char*>(buffer));
out_stream_->EndWriteData(bytes_to_copy);
if (bytes_to_copy == num_remaining_bytes) {
ResetReceiveBuffer();
} else {
receive_buffer_next_byte_pos_ += buffer_num_bytes;
}
out_stream_watcher_.ArmOrNotify();
return;
}
read_pending_ = true;
pending_write_buffer_ =
base::make_span(reinterpret_cast<char*>(buffer), buffer_num_bytes);
bluetooth_socket_->Receive(
buffer_num_bytes,
base::BindOnce(&BluetoothSerialPortImpl::OnBluetoothSocketReceive,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&BluetoothSerialPortImpl::OnBluetoothSocketReceiveError,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothSerialPortImpl::OnBluetoothSocketReceive(
int num_bytes_received,
scoped_refptr<net::IOBuffer> receive_buffer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GT(num_bytes_received, 0);
DCHECK(receive_buffer->data());
const bool receive_completed_after_flush = pending_write_buffer_.empty();
read_pending_ = false;
if (receive_completed_after_flush) {
if (out_stream_) {
receive_buffer_size_ = num_bytes_received;
receive_buffer_next_byte_pos_ = 0;
receive_buffer_ = std::move(receive_buffer);
ReadMore();
}
return;
}
DCHECK(out_stream_);
size_t bytes_to_copy = std::min(pending_write_buffer_.size(),
static_cast<size_t>(num_bytes_received));
std::copy(receive_buffer->data(), receive_buffer->data() + bytes_to_copy,
pending_write_buffer_.data());
out_stream_->EndWriteData(bytes_to_copy);
if (pending_write_buffer_.size() < static_cast<size_t>(num_bytes_received)) {
receive_buffer_size_ = num_bytes_received;
receive_buffer_next_byte_pos_ = pending_write_buffer_.size();
receive_buffer_ = std::move(receive_buffer);
}
ResetPendingWriteBuffer();
ReadMore();
}
void BluetoothSerialPortImpl::OnBluetoothSocketReceiveError(
BluetoothSocket::ErrorReason error_reason,
const std::string& error_message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
read_pending_ = false;
ResetPendingWriteBuffer();
if (out_stream_) {
if (client_) {
DCHECK(error_reason != BluetoothSocket::ErrorReason::kIOPending);
switch (error_reason) {
case BluetoothSocket::ErrorReason::kDisconnected:
client_->OnReadError(mojom::SerialReceiveError::DISCONNECTED);
break;
case BluetoothSocket::ErrorReason::kIOPending:
NOTREACHED();
break;
case BluetoothSocket::ErrorReason::kSystemError:
client_->OnReadError(mojom::SerialReceiveError::SYSTEM_ERROR);
break;
}
}
out_stream_->EndWriteData(0);
out_stream_watcher_.Cancel();
out_stream_.reset();
ResetReceiveBuffer();
}
}
void BluetoothSerialPortImpl::WriteToSocket(
MojoResult result,
const mojo::HandleSignalsState& state) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (result) {
case MOJO_RESULT_OK:
DCHECK(!write_pending_);
WriteMore();
break;
case MOJO_RESULT_SHOULD_WAIT:
in_stream_watcher_.ArmOrNotify();
break;
case MOJO_RESULT_FAILED_PRECONDITION:
case MOJO_RESULT_CANCELLED:
in_stream_watcher_.Cancel();
in_stream_.reset();
if (drain_callback_)
std::move(drain_callback_).Run();
break;
default:
NOTREACHED() << "Unexpected Mojo result: " << result;
}
}
void BluetoothSerialPortImpl::WriteMore() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(in_stream_.is_valid());
DCHECK(!write_pending_);
const void* buffer = nullptr;
uint32_t buffer_size = 0;
MojoResult result = in_stream_->BeginReadData(&buffer, &buffer_size,
MOJO_WRITE_DATA_FLAG_NONE);
if (result == MOJO_RESULT_SHOULD_WAIT) {
in_stream_watcher_.ArmOrNotify();
return;
}
if (result == MOJO_RESULT_FAILED_PRECONDITION) {
in_stream_watcher_.Cancel();
in_stream_.reset();
if (drain_callback_)
std::move(drain_callback_).Run();
return;
}
if (result != MOJO_RESULT_OK) {
in_stream_watcher_.Cancel();
in_stream_.reset();
return;
}
if (!bluetooth_socket_) {
mojo::ReportBadMessage("No Bluetooth socket.");
return;
}
write_pending_ = true;
auto io_buffer = base::MakeRefCounted<net::IOBuffer>(buffer_size);
std::copy(static_cast<const char*>(buffer),
static_cast<const char*>(buffer) + buffer_size, io_buffer->data());
bluetooth_socket_->Send(
io_buffer, buffer_size,
base::BindOnce(&BluetoothSerialPortImpl::OnBluetoothSocketSend,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&BluetoothSerialPortImpl::OnBluetoothSocketSendError,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothSerialPortImpl::OnBluetoothSocketSend(int num_bytes_sent) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_GE(num_bytes_sent, 0);
write_pending_ = false;
if (!flush_next_write_) {
DCHECK(in_stream_);
in_stream_->EndReadData(static_cast<uint32_t>(num_bytes_sent));
}
flush_next_write_ = false;
if (in_stream_) {
WriteMore();
}
}
void BluetoothSerialPortImpl::OnBluetoothSocketSendError(
const std::string& error_message) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
write_pending_ = false;
flush_next_write_ = false;
if (in_stream_) {
if (client_)
client_->OnSendError(mojom::SerialSendError::SYSTEM_ERROR);
in_stream_->EndReadData(0);
in_stream_watcher_.Cancel();
in_stream_.reset();
}
}
void BluetoothSerialPortImpl::OnSocketDisconnected(CloseCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(callback).Run();
bluetooth_socket_.reset();
delete this;
}
void BluetoothSerialPortImpl::Flush(mojom::SerialPortFlushMode mode,
FlushCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
switch (mode) {
case mojom::SerialPortFlushMode::kReceiveAndTransmit:
break;
case mojom::SerialPortFlushMode::kReceive:
if (read_pending_)
out_stream_->EndWriteData(0);
out_stream_watcher_.Cancel();
out_stream_.reset();
ResetReceiveBuffer();
ResetPendingWriteBuffer();
break;
case mojom::SerialPortFlushMode::kTransmit:
if (write_pending_) {
flush_next_write_ = true;
in_stream_->EndReadData(0);
}
in_stream_watcher_.Cancel();
in_stream_.reset();
break;
}
std::move(callback).Run();
}
void BluetoothSerialPortImpl::Drain(DrainCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!in_stream_) {
std::move(callback).Run();
return;
}
drain_callback_ = std::move(callback);
}
void BluetoothSerialPortImpl::GetControlSignals(
GetControlSignalsCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto signals = mojom::SerialPortControlSignals::New();
std::move(callback).Run(std::move(signals));
}
void BluetoothSerialPortImpl::SetControlSignals(
mojom::SerialHostControlSignalsPtr signals,
SetControlSignalsCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::move(callback).Run(true);
}
void BluetoothSerialPortImpl::ConfigurePort(
mojom::SerialConnectionOptionsPtr options,
ConfigurePortCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
options_ = std::move(options);
std::move(callback).Run(true);
}
void BluetoothSerialPortImpl::GetPortInfo(GetPortInfoCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto info = mojom::SerialConnectionInfo::New(
options_->bitrate, options_->data_bits,
options_->parity_bit, options_->stop_bits,
options_->cts_flow_control);
std::move(callback).Run(std::move(info));
}
void BluetoothSerialPortImpl::Close(bool flush, CloseCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bluetooth_socket_->Disconnect(
base::BindOnce(&BluetoothSerialPortImpl::OnSocketDisconnected,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
}