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 |