blob: d3798d848154da114a13828f8ffdf6dbbf781038 [file] [log] [blame]
// 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 "components/proximity_auth/bluetooth_connection.h"
#include <utility>
#include "base/bind.h"
#include "base/location.h"
#include "base/numerics/safe_conversions.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "components/cryptauth/remote_device.h"
#include "components/cryptauth/wire_message.h"
#include "components/proximity_auth/logging/logging.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "net/base/io_buffer.h"
namespace proximity_auth {
namespace {
const int kReceiveBufferSizeBytes = 1024;
}
BluetoothConnection::BluetoothConnection(
const cryptauth::RemoteDevice& remote_device,
const device::BluetoothUUID& uuid)
: cryptauth::Connection(remote_device),
uuid_(uuid),
weak_ptr_factory_(this) {}
BluetoothConnection::~BluetoothConnection() {
if (status() != DISCONNECTED)
Disconnect();
}
void BluetoothConnection::Connect() {
if (status() != DISCONNECTED) {
PA_LOG(WARNING)
<< "Ignoring attempt to connect a non-disconnected connection.";
return;
}
if (!device::BluetoothAdapterFactory::IsBluetoothSupported()) {
PA_LOG(WARNING)
<< "Connection failed: Bluetooth is unsupported on this platform.";
return;
}
SetStatus(IN_PROGRESS);
device::BluetoothAdapterFactory::GetAdapter(
base::Bind(&BluetoothConnection::OnAdapterInitialized,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothConnection::Disconnect() {
if (status() == DISCONNECTED) {
PA_LOG(WARNING)
<< "Ignoring attempt to disconnect a non-connected connection.";
return;
}
// Set status as disconnected now, rather than after the socket closes, so
// this connection is not reused.
SetStatus(DISCONNECTED);
if (socket_.get()) {
socket_->Disconnect(base::Bind(&base::DoNothing));
socket_ = NULL;
}
if (adapter_.get()) {
adapter_->RemoveObserver(this);
adapter_ = NULL;
}
}
void BluetoothConnection::SendMessageImpl(
std::unique_ptr<cryptauth::WireMessage> message) {
DCHECK_EQ(status(), CONNECTED);
// Serialize the message.
std::string serialized_message = message->Serialize();
int message_length = base::checked_cast<int>(serialized_message.size());
scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(message_length);
memcpy(buffer->data(), serialized_message.c_str(), message_length);
// Send it.
pending_message_ = std::move(message);
base::WeakPtr<BluetoothConnection> weak_this = weak_ptr_factory_.GetWeakPtr();
socket_->Send(buffer,
message_length,
base::Bind(&BluetoothConnection::OnSend, weak_this),
base::Bind(&BluetoothConnection::OnSendError, weak_this));
}
void BluetoothConnection::DeviceChanged(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
DCHECK_EQ(adapter, adapter_.get());
if (device->GetAddress() == remote_device().bluetooth_address &&
status() != DISCONNECTED && !device->IsConnected()) {
PA_LOG(INFO) << "Device disconnected...";
Disconnect();
}
}
void BluetoothConnection::DeviceRemoved(device::BluetoothAdapter* adapter,
device::BluetoothDevice* device) {
DCHECK_EQ(adapter, adapter_.get());
if (device->GetAddress() != remote_device().bluetooth_address)
return;
DCHECK_NE(status(), DISCONNECTED);
PA_LOG(INFO) << "Device disconnected...";
if (status() != DISCONNECTED)
Disconnect();
}
void BluetoothConnection::StartReceive() {
base::WeakPtr<BluetoothConnection> weak_this = weak_ptr_factory_.GetWeakPtr();
socket_->Receive(kReceiveBufferSizeBytes,
base::Bind(&BluetoothConnection::OnReceive, weak_this),
base::Bind(&BluetoothConnection::OnReceiveError, weak_this));
}
void BluetoothConnection::OnAdapterInitialized(
scoped_refptr<device::BluetoothAdapter> adapter) {
const std::string address = remote_device().bluetooth_address;
device::BluetoothDevice* bluetooth_device = adapter->GetDevice(address);
if (!bluetooth_device) {
PA_LOG(WARNING) << "Device with address " << address
<< " is not known to the system Bluetooth daemon.";
// TOOD(isherman): Optimistically attempt to seek the device and connect
// anyway, as was previously implemented in BluetoothConnectionFinder.
Disconnect();
return;
}
adapter_ = adapter;
adapter_->AddObserver(this);
base::WeakPtr<BluetoothConnection> weak_this = weak_ptr_factory_.GetWeakPtr();
bluetooth_device->ConnectToServiceInsecurely(
uuid_,
base::Bind(&BluetoothConnection::OnConnected, weak_this),
base::Bind(&BluetoothConnection::OnConnectionError, weak_this));
}
void BluetoothConnection::OnConnected(
scoped_refptr<device::BluetoothSocket> socket) {
if (status() != IN_PROGRESS) {
// This case is reachable if the client of |this| connection called
// |Disconnect()| while the backing Bluetooth connection was pending.
DCHECK_EQ(status(), DISCONNECTED);
PA_LOG(WARNING) << "Ignoring successful backend Bluetooth connection to an "
<< "already disconnected logical connection.";
return;
}
PA_LOG(INFO) << "Connection established with "
<< remote_device().bluetooth_address;
socket_ = socket;
SetStatus(CONNECTED);
StartReceive();
}
void BluetoothConnection::OnConnectionError(const std::string& error_message) {
PA_LOG(WARNING) << "Connection failed: " << error_message;
Disconnect();
}
void BluetoothConnection::OnSend(int bytes_sent) {
PA_LOG(INFO) << "Successfully sent " << bytes_sent << " bytes.";
OnDidSendMessage(*pending_message_, true);
pending_message_.reset();
}
void BluetoothConnection::OnSendError(const std::string& error_message) {
PA_LOG(WARNING) << "Error when sending bytes: " << error_message;
OnDidSendMessage(*pending_message_, false);
pending_message_.reset();
Disconnect();
}
void BluetoothConnection::OnReceive(int bytes_received,
scoped_refptr<net::IOBuffer> buffer) {
PA_LOG(INFO) << "Received " << bytes_received << " bytes.";
OnBytesReceived(std::string(buffer->data(), bytes_received));
// Post a task to delay the read until the socket is available, as
// calling StartReceive at this point would error with ERR_IO_PENDING.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&BluetoothConnection::StartReceive,
weak_ptr_factory_.GetWeakPtr()));
}
void BluetoothConnection::OnReceiveError(
device::BluetoothSocket::ErrorReason error_reason,
const std::string& error_message) {
PA_LOG(WARNING) << "Error receiving bytes: " << error_message;
// Post a task to delay the read until the socket is available, as
// calling StartReceive at this point would error with ERR_IO_PENDING.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(&BluetoothConnection::StartReceive,
weak_ptr_factory_.GetWeakPtr()));
}
} // namespace proximity_auth