| // 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 |