// 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 <objbase.h>
#include <string>
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "device/bluetooth/bluetooth_device_win.h"
#include "device/bluetooth/bluetooth_init_win.h"
#include "device/bluetooth/bluetooth_service_record_win.h"
#include "device/bluetooth/bluetooth_socket_thread.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 kInvalidRfcommPort[] = "Invalid RFCCOMM port.";
const char kFailedToCreateSocket[] = "Failed to create socket.";
const char kFailedToBindSocket[] = "Failed to bind socket.";
const char kFailedToListenOnSocket[] = "Failed to listen on socket.";
const char kFailedToGetSockNameForSocket[] = "Failed to getsockname.";
const char kFailedToAccept[] = "Failed to accept.";
const char kInvalidUUID[] = "Invalid UUID";
const char kWsaSetServiceError[] = "WSASetService error.";
std::string IPEndPointToBluetoothAddress(const net::IPEndPoint& end_point) {
if (end_point.address().size() != net::kBluetoothAddressSize)
return std::string();
// The address is copied from BTH_ADDR field of SOCKADDR_BTH, which is a
// 64-bit ULONGLONG that stores Bluetooth address in little-endian. Print in
// reverse order to preserve the correct ordering.
return base::StringPrintf("%02X:%02X:%02X:%02X:%02X:%02X",
end_point.address()[5],
end_point.address()[4],
end_point.address()[3],
end_point.address()[2],
end_point.address()[1],
end_point.address()[0]);
}
} // namespace
namespace device {
struct BluetoothSocketWin::ServiceRegData {
ServiceRegData() {
ZeroMemory(&address, sizeof(address));
ZeroMemory(&address_info, sizeof(address_info));
ZeroMemory(&uuid, sizeof(uuid));
ZeroMemory(&service, sizeof(service));
}
SOCKADDR_BTH address;
CSADDR_INFO address_info;
GUID uuid;
base::string16 name;
WSAQUERYSET service;
};
// static
scoped_refptr<BluetoothSocketWin>
BluetoothSocketWin::CreateBluetoothSocket(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
scoped_refptr<device::BluetoothSocketThread> socket_thread) {
DCHECK(ui_task_runner->RunsTasksOnCurrentThread());
return make_scoped_refptr(
new BluetoothSocketWin(ui_task_runner, socket_thread));
}
BluetoothSocketWin::BluetoothSocketWin(
scoped_refptr<base::SequencedTaskRunner> ui_task_runner,
scoped_refptr<BluetoothSocketThread> socket_thread)
: BluetoothSocketNet(ui_task_runner, socket_thread),
supports_rfcomm_(false),
rfcomm_channel_(0xFF),
bth_addr_(BTH_ADDR_NULL) {
}
BluetoothSocketWin::~BluetoothSocketWin() {
}
void BluetoothSocketWin::Connect(
const BluetoothDeviceWin* device,
const BluetoothUUID& uuid,
const base::Closure& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
DCHECK(device);
if (!uuid.IsValid()) {
error_callback.Run(kInvalidUUID);
return;
}
const BluetoothServiceRecordWin* service_record_win =
device->GetServiceRecord(uuid);
if (!service_record_win) {
error_callback.Run(kInvalidUUID);
return;
}
device_address_ = service_record_win->device_address();
if (service_record_win->SupportsRfcomm()) {
supports_rfcomm_ = true;
rfcomm_channel_ = service_record_win->rfcomm_channel();
bth_addr_ = service_record_win->device_bth_addr();
}
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::Listen(scoped_refptr<BluetoothAdapter> adapter,
const BluetoothUUID& uuid,
const BluetoothAdapter::ServiceOptions& options,
const base::Closure& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
adapter_ = adapter;
int rfcomm_channel = options.channel ? *options.channel : 0;
// TODO(xiyuan): Use |options.name|.
socket_thread()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&BluetoothSocketWin::DoListen,
this,
uuid,
rfcomm_channel,
success_callback,
error_callback));
}
void BluetoothSocketWin::ResetData() {
if (service_reg_data_) {
if (WSASetService(&service_reg_data_->service,RNRSERVICE_DELETE, 0) ==
SOCKET_ERROR) {
LOG(WARNING) << "Failed to unregister service.";
}
service_reg_data_.reset();
}
}
void BluetoothSocketWin::Accept(
const AcceptCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
socket_thread()->task_runner()->PostTask(
FROM_HERE,
base::Bind(&BluetoothSocketWin::DoAccept,
this,
success_callback,
error_callback));
}
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;
}
scoped_ptr<net::TCPSocket> scoped_socket(
new net::TCPSocket(NULL, net::NetLog::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_ << "): "
<< logging::SystemErrorCodeToString(error_code);
error_callback.Run("Error connecting to socket: " +
logging::SystemErrorCodeToString(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 =
scoped_socket->AdoptConnectedSocket(socket_fd, net::IPEndPoint());
if (net_result != net::OK) {
error_callback.Run("Error connecting to socket: " +
net::ErrorToString(net_result));
closesocket(socket_fd);
return;
}
SetTCPSocket(scoped_socket.Pass());
success_callback.Run();
}
void BluetoothSocketWin::DoListen(
const BluetoothUUID& uuid,
int rfcomm_channel,
const base::Closure& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
DCHECK(!tcp_socket() && !service_reg_data_);
// The valid range is 0-30. 0 means BT_PORT_ANY and 1-30 are the
// valid RFCOMM port numbers of SOCKADDR_BTH.
if (rfcomm_channel < 0 || rfcomm_channel > 30) {
LOG(WARNING) << "Failed to start service: "
<< "Invalid RFCCOMM port " << rfcomm_channel
<< ", uuid=" << uuid.value();
PostErrorCompletion(error_callback, kInvalidRfcommPort);
return;
}
SOCKET socket_fd = socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
if (socket_fd == INVALID_SOCKET) {
LOG(WARNING) << "Failed to start service: create socket, "
<< "winsock err=" << WSAGetLastError();
PostErrorCompletion(error_callback, kFailedToCreateSocket);
return;
}
// Note that |socket_fd| belongs to a non-TCP address family (i.e. AF_BTH),
// TCPSocket methods that involve address could not be called. So bind()
// is called on |socket_fd| directly.
scoped_ptr<net::TCPSocket> scoped_socket(
new net::TCPSocket(NULL, net::NetLog::Source()));
scoped_socket->AdoptListenSocket(socket_fd);
SOCKADDR_BTH sa;
struct sockaddr* sock_addr = reinterpret_cast<struct sockaddr*>(&sa);
int sock_addr_len = sizeof(sa);
ZeroMemory(&sa, sock_addr_len);
sa.addressFamily = AF_BTH;
sa.port = rfcomm_channel ? rfcomm_channel : BT_PORT_ANY;
if (bind(socket_fd, sock_addr, sock_addr_len) < 0) {
LOG(WARNING) << "Failed to start service: create socket, "
<< "winsock err=" << WSAGetLastError();
PostErrorCompletion(error_callback, kFailedToBindSocket);
return;
}
const int kListenBacklog = 5;
if (scoped_socket->Listen(kListenBacklog) < 0) {
LOG(WARNING) << "Failed to start service: Listen"
<< "winsock err=" << WSAGetLastError();
PostErrorCompletion(error_callback, kFailedToListenOnSocket);
return;
}
scoped_ptr<ServiceRegData> reg_data(new ServiceRegData);
reg_data->name = base::UTF8ToUTF16(uuid.canonical_value());
if (getsockname(socket_fd, sock_addr, &sock_addr_len)) {
LOG(WARNING) << "Failed to start service: getsockname, "
<< "winsock err=" << WSAGetLastError();
PostErrorCompletion(error_callback, kFailedToGetSockNameForSocket);
return;
}
reg_data->address = sa;
reg_data->address_info.LocalAddr.iSockaddrLength = sizeof(sa);
reg_data->address_info.LocalAddr.lpSockaddr =
reinterpret_cast<struct sockaddr*>(®_data->address);
reg_data->address_info.iSocketType = SOCK_STREAM;
reg_data->address_info.iProtocol = BTHPROTO_RFCOMM;
base::string16 cannonical_uuid = L"{" + base::ASCIIToUTF16(
uuid.canonical_value()) + L"}";
if (!SUCCEEDED(CLSIDFromString(cannonical_uuid.c_str(), ®_data->uuid))) {
LOG(WARNING) << "Failed to start service: "
<< ", invalid uuid=" << cannonical_uuid;
PostErrorCompletion(error_callback, kInvalidUUID);
return;
}
reg_data->service.dwSize = sizeof(WSAQUERYSET);
reg_data->service.lpszServiceInstanceName =
const_cast<LPWSTR>(reg_data->name.c_str());
reg_data->service.lpServiceClassId = ®_data->uuid;
reg_data->service.dwNameSpace = NS_BTH;
reg_data->service.dwNumberOfCsAddrs = 1;
reg_data->service.lpcsaBuffer = ®_data->address_info;
if (WSASetService(®_data->service,
RNRSERVICE_REGISTER, 0) == SOCKET_ERROR) {
LOG(WARNING) << "Failed to register profile: WSASetService"
<< "winsock err=" << WSAGetLastError();
PostErrorCompletion(error_callback, kWsaSetServiceError);
return;
}
SetTCPSocket(scoped_socket.Pass());
service_reg_data_ = reg_data.Pass();
PostSuccess(success_callback);
}
void BluetoothSocketWin::DoAccept(
const AcceptCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
int result = tcp_socket()->Accept(
&accept_socket_,
&accept_address_,
base::Bind(&BluetoothSocketWin::OnAcceptOnSocketThread,
this,
success_callback,
error_callback));
if (result != net::OK && result != net::ERR_IO_PENDING) {
LOG(WARNING) << "Failed to accept, net err=" << result;
PostErrorCompletion(error_callback, kFailedToAccept);
}
}
void BluetoothSocketWin::OnAcceptOnSocketThread(
const AcceptCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback,
int accept_result) {
DCHECK(socket_thread()->task_runner()->RunsTasksOnCurrentThread());
if (accept_result != net::OK) {
LOG(WARNING) << "OnAccept error, net err=" << accept_result;
PostErrorCompletion(error_callback, kFailedToAccept);
return;
}
ui_task_runner()->PostTask(
FROM_HERE,
base::Bind(&BluetoothSocketWin::OnAcceptOnUI,
this,
base::Passed(&accept_socket_),
accept_address_,
success_callback,
error_callback));
}
void BluetoothSocketWin::OnAcceptOnUI(
scoped_ptr<net::TCPSocket> accept_socket,
const net::IPEndPoint& peer_address,
const AcceptCompletionCallback& success_callback,
const ErrorCompletionCallback& error_callback) {
DCHECK(ui_task_runner()->RunsTasksOnCurrentThread());
const std::string peer_device_address =
IPEndPointToBluetoothAddress(peer_address);
const BluetoothDevice* peer_device = adapter_->GetDevice(peer_device_address);
if (!peer_device) {
LOG(WARNING) << "OnAccept failed with unknown device, addr="
<< peer_device_address;
error_callback.Run(kFailedToAccept);
return;
}
scoped_refptr<BluetoothSocketWin> peer_socket =
CreateBluetoothSocket(ui_task_runner(), socket_thread());
peer_socket->SetTCPSocket(accept_socket.Pass());
success_callback.Run(peer_device, peer_socket);
}
} // namespace device