blob: 9b1e953fb6ddb709705726698625c3232bccd08a [file] [log] [blame]
// Copyright (c) 2012 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_win.h"
#include <string>
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/sys_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "device/bluetooth/bluetooth_init_win.h"
#include "device/bluetooth/bluetooth_service_record_win.h"
#include "device/bluetooth/bluetooth_socket_thread_win.h"
#include "net/base/io_buffer.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/base/winsock_init.h"
namespace {
const char kL2CAPNotSupported[] = "Bluetooth L2CAP protocal is not supported";
const char kSocketAlreadyConnected[] = "Socket is already connected.";
const char kSocketNotConnected[] = "Socket is not connected.";
using device::BluetoothSocketWin;
std::string FormatErrorMessage(DWORD error_code) {
TCHAR error_msg[1024];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
0,
error_code,
0,
error_msg,
1024,
NULL);
return base::SysWideToUTF8(error_msg);
}
static void DeactivateSocket(
const scoped_refptr<device::BluetoothSocketThreadWin>& socket_thread) {
socket_thread->OnSocketDeactivate();
}
} // namespace
namespace device {
// static
scoped_refptr<BluetoothSocketWin> BluetoothSocketWin::CreateBluetoothSocket(
const BluetoothServiceRecord& service_record,
scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
scoped_refptr<BluetoothSocketThreadWin> socket_thread,
net::NetLog* net_log,
const net::NetLog::Source& source) {
DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
const BluetoothServiceRecordWin* service_record_win =
static_cast<const BluetoothServiceRecordWin*>(&service_record);
scoped_refptr<BluetoothSocketWin> result(
new BluetoothSocketWin(ui_task_runner, socket_thread, net_log, source));
result->device_address_ = service_record_win->address();
if (service_record.SupportsRfcomm()) {
result->supports_rfcomm_ = true;
result->rfcomm_channel_ = service_record_win->rfcomm_channel();
result->bth_addr_ = service_record_win->bth_addr();
}
return result;
}
BluetoothSocketWin::BluetoothSocketWin(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
scoped_refptr<BluetoothSocketThreadWin> socket_thread,
net::NetLog* net_log,
const net::NetLog::Source& source)
: ui_task_runner_(ui_task_runner),
socket_thread_(socket_thread),
net_log_(net_log),
source_(source),
supports_rfcomm_(false),
rfcomm_channel_(-1),
bth_addr_(BTH_ADDR_NULL) {
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
socket_thread->OnSocketActivate();
}
BluetoothSocketWin::~BluetoothSocketWin() {
ui_task_runner_->PostTask(FROM_HERE,
base::Bind(&DeactivateSocket, socket_thread_));
}
void BluetoothSocketWin::Close() {
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
socket_thread_->task_runner()->PostTask(
FROM_HERE, base::Bind(&BluetoothSocketWin::DoClose, this));
}
void BluetoothSocketWin::Connect(
const base::Closure& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
socket_thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(
&BluetoothSocketWin::DoConnect,
this,
base::Bind(&BluetoothSocketWin::PostSuccess, this, success_callback),
base::Bind(
&BluetoothSocketWin::PostErrorCompletion, this, error_callback)));
}
void BluetoothSocketWin::Disconnect(const base::Closure& success_callback) {
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
socket_thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(
&BluetoothSocketWin::DoDisconnect,
this,
base::Bind(
&BluetoothSocketWin::PostSuccess, this, success_callback)));
}
void BluetoothSocketWin::Receive(
int count,
const ReceiveCompletionCallback& success_callback,
const ReceiveErrorCompletionCallback& error_callback) {
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
socket_thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(&BluetoothSocketWin::DoReceive,
this,
count,
base::Bind(&BluetoothSocketWin::PostReceiveCompletion,
this,
success_callback),
base::Bind(&BluetoothSocketWin::PostReceiveErrorCompletion,
this,
error_callback)));
}
void BluetoothSocketWin::Send(scoped_refptr<net::IOBuffer> buffer,
int buffer_size,
const SendCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(ui_task_runner_->RunsTasksOnCurrentThread());
socket_thread_->task_runner()->PostTask(
FROM_HERE,
base::Bind(
&BluetoothSocketWin::DoSend,
this,
buffer,
buffer_size,
base::Bind(
&BluetoothSocketWin::PostSendCompletion, this, success_callback),
base::Bind(
&BluetoothSocketWin::PostErrorCompletion, this, error_callback)));
}
void BluetoothSocketWin::DoClose() {
DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
base::ThreadRestrictions::AssertIOAllowed();
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;
std::queue<linked_ptr<WriteRequest> > empty;
write_queue_.swap(empty);
}
void BluetoothSocketWin::DoConnect(
const base::Closure& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
base::ThreadRestrictions::AssertIOAllowed();
if (tcp_socket_) {
error_callback.Run(kSocketAlreadyConnected);
return;
}
if (!supports_rfcomm_) {
// TODO(youngki) add support for L2CAP sockets as well.
error_callback.Run(kL2CAPNotSupported);
return;
}
tcp_socket_.reset(new net::TCPSocket(net_log_, source_));
net::EnsureWinsockInit();
SOCKET socket_fd = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
SOCKADDR_BTH sa;
ZeroMemory(&sa, sizeof(sa));
sa.addressFamily = AF_BTH;
sa.port = rfcomm_channel_;
sa.btAddr = bth_addr_;
// TODO(rpaquay): Condider making this call non-blocking.
int status = connect(socket_fd, reinterpret_cast<SOCKADDR*>(&sa), sizeof(sa));
DWORD error_code = WSAGetLastError();
if (!(status == 0 || error_code == WSAEINPROGRESS)) {
LOG(ERROR) << "Failed to connect bluetooth socket "
<< "(" << device_address_ << "): "
<< "(" << error_code << ")" << FormatErrorMessage(error_code);
error_callback.Run("Error connecting to socket: " +
FormatErrorMessage(error_code));
closesocket(socket_fd);
return;
}
// Note: We don't have a meaningful |IPEndPoint|, but that is ok since the
// TCPSocket implementation does not actually require one.
int net_result =
tcp_socket_->AdoptConnectedSocket(socket_fd, net::IPEndPoint());
if (net_result != net::OK) {
error_callback.Run("Error connecting to socket: " +
std::string(net::ErrorToString(net_result)));
closesocket(socket_fd);
return;
}
success_callback.Run();
}
void BluetoothSocketWin::DoDisconnect(const base::Closure& success_callback) {
DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
base::ThreadRestrictions::AssertIOAllowed();
DoClose();
success_callback.Run();
}
void BluetoothSocketWin::DoReceive(
int count,
const ReceiveCompletionCallback& success_callback,
const ReceiveErrorCompletionCallback& error_callback) {
DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
base::ThreadRestrictions::AssertIOAllowed();
if (!tcp_socket_) {
error_callback.Run(BluetoothSocketWin::kDisconnected, kSocketNotConnected);
return;
}
// Only one pending read at a time
if (read_buffer_.get()) {
error_callback.Run(BluetoothSocketWin::kIOPending,
net::ErrorToString(net::ERR_IO_PENDING));
return;
}
scoped_refptr<net::IOBufferWithSize> buffer(new net::IOBufferWithSize(count));
int read_result =
tcp_socket_->Read(buffer.get(),
buffer->size(),
base::Bind(&BluetoothSocketWin::OnSocketReadComplete,
this,
success_callback,
error_callback));
if (read_result > 0) {
success_callback.Run(read_result, buffer);
} else if (read_result == net::OK ||
read_result == net::ERR_CONNECTION_CLOSED) {
error_callback.Run(BluetoothSocketWin::kDisconnected,
net::ErrorToString(net::ERR_CONNECTION_CLOSED));
} else if (read_result == net::ERR_IO_PENDING) {
read_buffer_ = buffer;
} else {
error_callback.Run(BluetoothSocketWin::kSystemError,
net::ErrorToString(read_result));
}
}
void BluetoothSocketWin::OnSocketReadComplete(
const ReceiveCompletionCallback& success_callback,
const ReceiveErrorCompletionCallback& error_callback,
int read_result) {
DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
base::ThreadRestrictions::AssertIOAllowed();
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) {
error_callback.Run(BluetoothSocketWin::kDisconnected,
net::ErrorToString(net::ERR_CONNECTION_CLOSED));
} else {
error_callback.Run(BluetoothSocketWin::kSystemError,
net::ErrorToString(read_result));
}
}
void BluetoothSocketWin::DoSend(scoped_refptr<net::IOBuffer> buffer,
int buffer_size,
const SendCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
base::ThreadRestrictions::AssertIOAllowed();
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 BluetoothSocketWin::SendFrontWriteRequest() {
DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
base::ThreadRestrictions::AssertIOAllowed();
if (!tcp_socket_)
return;
if (write_queue_.size() == 0)
return;
linked_ptr<WriteRequest> request = write_queue_.front();
net::CompletionCallback callback =
base::Bind(&BluetoothSocketWin::OnSocketWriteComplete,
this,
request->success_callback,
request->error_callback);
int send_result =
tcp_socket_->Write(request->buffer, request->buffer_size, callback);
if (send_result != net::ERR_IO_PENDING) {
callback.Run(send_result);
}
}
void BluetoothSocketWin::OnSocketWriteComplete(
const SendCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback,
int send_result) {
DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread());
base::ThreadRestrictions::AssertIOAllowed();
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(&BluetoothSocketWin::SendFrontWriteRequest, this));
}
void BluetoothSocketWin::PostSuccess(const base::Closure& callback) {
ui_task_runner_->PostTask(FROM_HERE, callback);
}
void BluetoothSocketWin::PostErrorCompletion(
const ErrorCompletionCallback& callback,
const std::string& error) {
ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, error));
}
void BluetoothSocketWin::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 BluetoothSocketWin::PostReceiveErrorCompletion(
const ReceiveErrorCompletionCallback& callback,
ErrorReason reason,
const std::string& error_message) {
ui_task_runner_->PostTask(FROM_HERE,
base::Bind(callback, reason, error_message));
}
void BluetoothSocketWin::PostSendCompletion(
const SendCompletionCallback& callback,
int bytes_written) {
ui_task_runner_->PostTask(FROM_HERE, base::Bind(callback, bytes_written));
}
} // namespace device