// 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 #include #include #include "base/location.h" #include "base/logging.h" #include "base/memory/linked_ptr.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.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.h" namespace { const char kSocketNotConnected[] = "Socket is not connected."; static void DeactivateSocket( const scoped_refptr& socket_thread) { socket_thread->OnSocketDeactivate(); } } // namespace namespace device { BluetoothSocketNet::WriteRequest::WriteRequest() : buffer_size(0) {} BluetoothSocketNet::WriteRequest::~WriteRequest() {} BluetoothSocketNet::BluetoothSocketNet( scoped_refptr ui_task_runner, scoped_refptr socket_thread) : ui_task_runner_(ui_task_runner), socket_thread_(socket_thread) { DCHECK(ui_task_runner->RunsTasksOnCurrentThread()); socket_thread_->OnSocketActivate(); } BluetoothSocketNet::~BluetoothSocketNet() { DCHECK(tcp_socket_.get() == NULL); ui_task_runner_->PostTask(FROM_HERE, base::Bind(&DeactivateSocket, socket_thread_)); } void BluetoothSocketNet::Close() { DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); socket_thread_->task_runner()->PostTask( FROM_HERE, base::Bind(&BluetoothSocketNet::DoClose, this)); } void BluetoothSocketNet::Disconnect( const base::Closure& success_callback) { DCHECK(ui_task_runner_->RunsTasksOnCurrentThread()); 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_->RunsTasksOnCurrentThread()); 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 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( &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, net::NetLog::Source())); } void BluetoothSocketNet::SetTCPSocket(scoped_ptr 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()->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 > empty; std::swap(write_queue_, empty); ResetData(); } void BluetoothSocketNet::DoDisconnect(const base::Closure& callback) { DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread()); base::ThreadRestrictions::AssertIOAllowed(); DoClose(); callback.Run(); } void BluetoothSocketNet::DoReceive( int buffer_size, const ReceiveCompletionCallback& success_callback, const ReceiveErrorCompletionCallback& error_callback) { DCHECK(socket_thread_->task_runner()->RunsTasksOnCurrentThread()); base::ThreadRestrictions::AssertIOAllowed(); 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 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()->RunsTasksOnCurrentThread()); base::ThreadRestrictions::AssertIOAllowed(); scoped_refptr 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 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 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()->RunsTasksOnCurrentThread()); base::ThreadRestrictions::AssertIOAllowed(); if (!tcp_socket_) return; if (write_queue_.size() == 0) return; linked_ptr request = write_queue_.front(); net::CompletionCallback callback = base::Bind(&BluetoothSocketNet::OnSocketWriteComplete, this, request->success_callback, request->error_callback); int send_result = tcp_socket_->Write(request->buffer.get(), request->buffer_size, callback); 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()->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(&BluetoothSocketNet::SendFrontWriteRequest, this)); } void BluetoothSocketNet::PostReceiveCompletion( const ReceiveCompletionCallback& callback, int io_buffer_size, scoped_refptr 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