| Index: device/bluetooth/bluetooth_socket_win.cc
|
| diff --git a/device/bluetooth/bluetooth_socket_win.cc b/device/bluetooth/bluetooth_socket_win.cc
|
| index a684d14cffd864e3d622cf6c6ff360f94027cc7c..4277e6643440c130ada50e0ca0a0bdbf1b5865df 100644
|
| --- a/device/bluetooth/bluetooth_socket_win.cc
|
| +++ b/device/bluetooth/bluetooth_socket_win.cc
|
| @@ -9,13 +9,21 @@
|
| #include "base/logging.h"
|
| #include "base/memory/ref_counted.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 "net/base/io_buffer.h"
|
| +#include "net/base/ip_endpoint.h"
|
| +#include "net/base/net_errors.h"
|
| #include "net/base/winsock_init.h"
|
| +#include "net/socket/tcp_socket_win.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.";
|
| +
|
| std::string FormatErrorMessage(DWORD error_code) {
|
| TCHAR error_msg[1024];
|
| FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
|
| @@ -32,84 +40,242 @@ std::string FormatErrorMessage(DWORD error_code) {
|
|
|
| namespace device {
|
|
|
| -BluetoothSocketWin::BluetoothSocketWin(SOCKET fd) : fd_(fd) {
|
| -}
|
| +BluetoothSocketWin::BluetoothSocketWin(
|
| + scoped_refptr<base::SequencedTaskRunner> task_runner,
|
| + net::NetLog* net_log,
|
| + const net::NetLog::Source& source)
|
| + : task_runner_(task_runner),
|
| + net_log_(net_log),
|
| + source_(source),
|
| + supports_rfcomm_(false),
|
| + rfcomm_channel_(-1),
|
| + bth_addr_(BTH_ADDR_NULL) {}
|
|
|
| BluetoothSocketWin::~BluetoothSocketWin() {
|
| - closesocket(fd_);
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| }
|
|
|
| // static
|
| scoped_refptr<BluetoothSocket> BluetoothSocketWin::CreateBluetoothSocket(
|
| - const BluetoothServiceRecord& service_record) {
|
| - BluetoothSocketWin* bluetooth_socket = NULL;
|
| + const BluetoothServiceRecord& service_record,
|
| + scoped_refptr<base::SequencedTaskRunner> task_runner,
|
| + net::NetLog* net_log,
|
| + const net::NetLog::Source& source) {
|
| + const BluetoothServiceRecordWin* service_record_win =
|
| + static_cast<const BluetoothServiceRecordWin*>(&service_record);
|
| +
|
| + scoped_refptr<BluetoothSocketWin> result(
|
| + new BluetoothSocketWin(task_runner, net_log, source));
|
| + result->device_address_ = service_record_win->address();
|
| if (service_record.SupportsRfcomm()) {
|
| - 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 = service_record.rfcomm_channel();
|
| - const BluetoothServiceRecordWin* service_record_win =
|
| - static_cast<const BluetoothServiceRecordWin*>(&service_record);
|
| - sa.btAddr = service_record_win->bth_addr();
|
| -
|
| - int status = connect(socket_fd,
|
| - reinterpret_cast<SOCKADDR *>(&sa),
|
| - sizeof(sa));
|
| - DWORD error_code = WSAGetLastError();
|
| - if (status == 0 || error_code == WSAEINPROGRESS) {
|
| - bluetooth_socket =
|
| - new BluetoothSocketWin(socket_fd);
|
| - } else {
|
| - LOG(ERROR) << "Failed to connect bluetooth socket "
|
| - << "(" << service_record.address() << "): "
|
| - << "(" << error_code << ")" << FormatErrorMessage(error_code);
|
| - closesocket(socket_fd);
|
| - }
|
| + result->supports_rfcomm_ = true;
|
| + result->rfcomm_channel_ = service_record_win->rfcomm_channel();
|
| + result->bth_addr_ = service_record_win->bth_addr();
|
| }
|
| - // TODO(youngki) add support for L2CAP sockets as well.
|
|
|
| - return scoped_refptr<BluetoothSocketWin>(bluetooth_socket);
|
| + // Underlying socket is used on a different thread.
|
| + result->thread_checker_.DetachFromThread();
|
| + return result;
|
| }
|
|
|
| -bool BluetoothSocketWin::Receive(net::GrowableIOBuffer* buffer) {
|
| - buffer->SetCapacity(1024);
|
| - int bytes_read;
|
| - do {
|
| - if (buffer->RemainingCapacity() == 0)
|
| - buffer->SetCapacity(buffer->capacity() * 2);
|
| - bytes_read = recv(fd_, buffer->data(), buffer->RemainingCapacity(), 0);
|
| - if (bytes_read > 0)
|
| - buffer->set_offset(buffer->offset() + bytes_read);
|
| - } while (bytes_read > 0);
|
| +void BluetoothSocketWin::Connect(
|
| + const base::Closure& success_callback,
|
| + const ErrorCompletionCallback& error_callback) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
|
|
| + if (socket_win_) {
|
| + error_callback.Run(kSocketAlreadyConnected);
|
| + return;
|
| + }
|
| +
|
| + if (!supports_rfcomm_) {
|
| + // TODO(youngki) add support for L2CAP sockets as well.
|
| + error_callback.Run(kL2CAPNotSupported);
|
| + return;
|
| + }
|
| +
|
| + socket_win_.reset(new net::TCPSocketWin(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 (bytes_read < 0 && error_code != WSAEWOULDBLOCK) {
|
| - error_message_ = FormatErrorMessage(error_code);
|
| - return false;
|
| + 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
|
| + // TCPSocketWin implementation does not actually require one.
|
| + int net_result =
|
| + socket_win_->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::Disconnect(const base::Closure& success_callback) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
| +
|
| + if (socket_win_) {
|
| + socket_win_->Close();
|
| + socket_win_.reset(NULL);
|
| }
|
| - return true;
|
| +
|
| + // Note: Closing |socket_win_| 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);
|
| +
|
| + success_callback.Run();
|
| }
|
|
|
| -bool BluetoothSocketWin::Send(net::DrainableIOBuffer* buffer) {
|
| - int bytes_written;
|
| - do {
|
| - bytes_written = send(fd_, buffer->data(), buffer->BytesRemaining(), 0);
|
| - if (bytes_written > 0)
|
| - buffer->DidConsume(bytes_written);
|
| - } while (buffer->BytesRemaining() > 0 && bytes_written > 0);
|
| +void BluetoothSocketWin::Receive(
|
| + int count,
|
| + const ReceiveCompletionCallback& success_callback,
|
| + const ReceiveErrorCompletionCallback& error_callback) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
|
|
| - DWORD error_code = WSAGetLastError();
|
| - if (bytes_written < 0 && error_code != WSAEWOULDBLOCK) {
|
| - error_message_ = FormatErrorMessage(error_code);
|
| - return false;
|
| + if (!socket_win_) {
|
| + 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 =
|
| + socket_win_->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(thread_checker_.CalledOnValidThread());
|
| + 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::Send(scoped_refptr<net::DrainableIOBuffer> buffer,
|
| + const SendCompletionCallback& success_callback,
|
| + const ErrorCompletionCallback& error_callback) {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
| +
|
| + if (!socket_win_) {
|
| + error_callback.Run(kSocketNotConnected);
|
| + return;
|
| + }
|
| +
|
| + linked_ptr<WriteRequest> request(new WriteRequest());
|
| + request->buffer = buffer;
|
| + request->success_callback = success_callback;
|
| + request->error_callback = error_callback;
|
| +
|
| + write_queue_.push(request);
|
| + if (write_queue_.size() == 1) {
|
| + SendFrontWriteRequest();
|
| }
|
| - return true;
|
| }
|
|
|
| -std::string BluetoothSocketWin::GetLastErrorMessage() const {
|
| - return error_message_;
|
| +void BluetoothSocketWin::SendFrontWriteRequest() {
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + base::ThreadRestrictions::AssertIOAllowed();
|
| +
|
| + if (!socket_win_)
|
| + 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 =
|
| + socket_win_->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(thread_checker_.CalledOnValidThread());
|
| + 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.
|
| + task_runner_->PostNonNestableTask(
|
| + FROM_HERE, base::Bind(&BluetoothSocketWin::SendFrontWriteRequest, this));
|
| }
|
|
|
| } // namespace device
|
|
|