blob: 97beefd7e912c6cbae06ea07888620d33d97c90e [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/bluetooth/bluetooth_socket_net.h"
#include <memory>
#include <string>
#include <utility>
#include "base/containers/queue.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
#include "base/threading/thread_restrictions.h"
#include "device/bluetooth/bluetooth_socket.h"
#include "device/bluetooth/bluetooth_socket_thread.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "net/log/net_log_source.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
namespace {
const char kSocketNotConnected[] = "Socket is not connected.";
static void DeactivateSocket(
const scoped_refptr<device::BluetoothSocketThread>& socket_thread) {
socket_thread->OnSocketDeactivate();
}
} // namespace
namespace device {
BluetoothSocketNet::WriteRequest::WriteRequest()
: buffer_size(0) {}
BluetoothSocketNet::WriteRequest::~WriteRequest() = default;
BluetoothSocketNet::BluetoothSocketNet(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
scoped_refptr<BluetoothSocketThread> socket_thread)
: ui_task_runner_(ui_task_runner),
socket_thread_(socket_thread) {
DCHECK(ui_task_runner->RunsTasksInCurrentSequence());
socket_thread_->OnSocketActivate();
}
BluetoothSocketNet::~BluetoothSocketNet() {
DCHECK(!tcp_socket_);
ui_task_runner_->PostTask(FROM_HERE,
base::Bind(&DeactivateSocket, socket_thread_));
}
void BluetoothSocketNet::Close() {
DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
socket_thread_->task_runner()->PostTask(
FROM_HERE, base::Bind(&BluetoothSocketNet::DoClose, this));
}
void BluetoothSocketNet::Disconnect(
const base::Closure& success_callback) {
DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
socket_thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(
&BluetoothSocketNet::DoDisconnect,
this,
base::Bind(&BluetoothSocketNet::PostSuccess,
this,
success_callback)));
}
void BluetoothSocketNet::Receive(
int buffer_size,
const ReceiveCompletionCallback& success_callback,
const ReceiveErrorCompletionCallback& error_callback) {
DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
socket_thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(
&BluetoothSocketNet::DoReceive,
this,
buffer_size,
base::Bind(&BluetoothSocketNet::PostReceiveCompletion,
this,
success_callback),
base::Bind(&BluetoothSocketNet::PostReceiveErrorCompletion,
this,
error_callback)));
}
void BluetoothSocketNet::Send(
scoped_refptr<net::IOBuffer> buffer,
int buffer_size,
const SendCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(ui_task_runner_->RunsTasksInCurrentSequence());
socket_thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(
&BluetoothSocketNet::DoSend,
this,
buffer,
buffer_size,
base::Bind(&BluetoothSocketNet::PostSendCompletion,
this,
success_callback),
base::Bind(&BluetoothSocketNet::PostErrorCompletion,
this,
error_callback)));
}
void BluetoothSocketNet::ResetData() {
}
void BluetoothSocketNet::ResetTCPSocket() {
tcp_socket_.reset(new net::TCPSocket(NULL, NULL, net::NetLogSource()));
}
void BluetoothSocketNet::SetTCPSocket(
std::unique_ptr<net::TCPSocket> tcp_socket) {
tcp_socket_ = std::move(tcp_socket);
}
void BluetoothSocketNet::PostSuccess(const base::Closure& callback) {
ui_task_runner_->PostTask(FROM_HERE, callback);
}
void BluetoothSocketNet::PostErrorCompletion(
const ErrorCompletionCallback& callback,
const std::string& error) {
ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, error));
}
void BluetoothSocketNet::DoClose() {
DCHECK(socket_thread_->task_runner()->RunsTasksInCurrentSequence());
base::AssertBlockingAllowed();
if (tcp_socket_) {
tcp_socket_->Close();
tcp_socket_.reset(NULL);
}
// Note: Closing |tcp_socket_| above released all potential pending
// Send/Receive operations, so we can no safely release the state associated
// to those pending operations.
read_buffer_ = NULL;
base::queue<linked_ptr<WriteRequest>> empty;
std::swap(write_queue_, empty);
ResetData();
}
void BluetoothSocketNet::DoDisconnect(const base::Closure& callback) {
DCHECK(socket_thread_->task_runner()->RunsTasksInCurrentSequence());
base::AssertBlockingAllowed();
DoClose();
callback.Run();
}
void BluetoothSocketNet::DoReceive(
int buffer_size,
const ReceiveCompletionCallback& success_callback,
const ReceiveErrorCompletionCallback& error_callback) {
DCHECK(socket_thread_->task_runner()->RunsTasksInCurrentSequence());
base::AssertBlockingAllowed();
if (!tcp_socket_) {
error_callback.Run(BluetoothSocket::kDisconnected, kSocketNotConnected);
return;
}
// Only one pending read at a time
if (read_buffer_.get()) {
error_callback.Run(BluetoothSocket::kIOPending,
net::ErrorToString(net::ERR_IO_PENDING));
return;
}
scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(buffer_size));
int read_result =
tcp_socket_->Read(buffer.get(),
buffer->size(),
base::Bind(&BluetoothSocketNet::OnSocketReadComplete,
this,
success_callback,
error_callback));
read_buffer_ = buffer;
if (read_result != net::ERR_IO_PENDING)
OnSocketReadComplete(success_callback, error_callback, read_result);
}
void BluetoothSocketNet::OnSocketReadComplete(
const ReceiveCompletionCallback& success_callback,
const ReceiveErrorCompletionCallback& error_callback,
int read_result) {
DCHECK(socket_thread_->task_runner()->RunsTasksInCurrentSequence());
base::AssertBlockingAllowed();
scoped_refptr<net::IOBufferWithSize> buffer;
buffer.swap(read_buffer_);
if (read_result > 0) {
success_callback.Run(read_result, buffer);
} else if (read_result == net::OK ||
read_result == net::ERR_CONNECTION_CLOSED ||
read_result == net::ERR_CONNECTION_RESET) {
error_callback.Run(BluetoothSocket::kDisconnected,
net::ErrorToString(read_result));
} else {
error_callback.Run(BluetoothSocket::kSystemError,
net::ErrorToString(read_result));
}
}
void BluetoothSocketNet::DoSend(
scoped_refptr<net::IOBuffer> buffer,
int buffer_size,
const SendCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(socket_thread_->task_runner()->RunsTasksInCurrentSequence());
base::AssertBlockingAllowed();
if (!tcp_socket_) {
error_callback.Run(kSocketNotConnected);
return;
}
linked_ptr<WriteRequest> request(new WriteRequest());
request->buffer = buffer;
request->buffer_size = buffer_size;
request->success_callback = success_callback;
request->error_callback = error_callback;
write_queue_.push(request);
if (write_queue_.size() == 1) {
SendFrontWriteRequest();
}
}
void BluetoothSocketNet::SendFrontWriteRequest() {
DCHECK(socket_thread_->task_runner()->RunsTasksInCurrentSequence());
base::AssertBlockingAllowed();
if (!tcp_socket_)
return;
if (write_queue_.size() == 0)
return;
linked_ptr<WriteRequest> request = write_queue_.front();
net::CompletionCallback callback =
base::Bind(&BluetoothSocketNet::OnSocketWriteComplete,
this,
request->success_callback,
request->error_callback);
net::NetworkTrafficAnnotationTag traffic_annotation =
net::DefineNetworkTrafficAnnotation("bluetooth_socket", R"(
semantics {
sender: "Bluetooth Socket"
description:
"This socket connects to a bluetooth device for local data "
"transfer."
trigger:
"When user selects to connect to a bluetooth device or communicate "
"with it."
data:
"Any data that needs to be sent to a bluetooth device."
destination: OTHER
destination_other: "Data is sent to a bluetooth device."
}
policy {
cookies_allowed: NO
setting:
"This feature cannot be disabled in settings, but it will not be "
"used if bluetooth connections are not made."
policy_exception_justification:
"DeviceAllowBluetooth policy can disable Bluetooth for ChromeOS, "
"not implemented for other platforms."
})");
int send_result =
tcp_socket_->Write(request->buffer.get(), request->buffer_size, callback,
traffic_annotation);
if (send_result != net::ERR_IO_PENDING) {
callback.Run(send_result);
}
}
void BluetoothSocketNet::OnSocketWriteComplete(
const SendCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback,
int send_result) {
DCHECK(socket_thread_->task_runner()->RunsTasksInCurrentSequence());
base::AssertBlockingAllowed();
write_queue_.pop();
if (send_result >= net::OK) {
success_callback.Run(send_result);
} else {
error_callback.Run(net::ErrorToString(send_result));
}
// Don't call directly to avoid potentail large recursion.
socket_thread_->task_runner()->PostNonNestableTask(
FROM_HERE,
base::Bind(&BluetoothSocketNet::SendFrontWriteRequest, this));
}
void BluetoothSocketNet::PostReceiveCompletion(
const ReceiveCompletionCallback& callback,
int io_buffer_size,
scoped_refptr<net::IOBuffer> io_buffer) {
ui_task_runner_->PostTask(FROM_HERE,
base::Bind(callback, io_buffer_size, io_buffer));
}
void BluetoothSocketNet::PostReceiveErrorCompletion(
const ReceiveErrorCompletionCallback& callback,
ErrorReason reason,
const std::string& error_message) {
ui_task_runner_->PostTask(FROM_HERE,
base::Bind(callback, reason, error_message));
}
void BluetoothSocketNet::PostSendCompletion(
const SendCompletionCallback& callback,
int bytes_written) {
ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, bytes_written));
}
} // namespace device