1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "components/proximity_auth/bluetooth_connection.h"
6
7#include "base/bind.h"
8#include "base/message_loop/message_loop.h"
9#include "base/numerics/safe_conversions.h"
10#include "components/proximity_auth/bluetooth_util.h"
11#include "components/proximity_auth/remote_device.h"
12#include "components/proximity_auth/wire_message.h"
13#include "device/bluetooth/bluetooth_adapter_factory.h"
14#include "device/bluetooth/bluetooth_device.h"
15#include "net/base/io_buffer.h"
16
17namespace proximity_auth {
18namespace {
19const int kReceiveBufferSizeBytes = 1024;
20}
21
22BluetoothConnection::BluetoothConnection(const RemoteDevice& remote_device,
23                                         const device::BluetoothUUID& uuid)
24    : Connection(remote_device), uuid_(uuid), weak_ptr_factory_(this) {
25}
26
27BluetoothConnection::~BluetoothConnection() {
28  Disconnect();
29}
30
31void BluetoothConnection::Connect() {
32  if (status() != DISCONNECTED) {
33    VLOG(1)
34        << "[BC] Ignoring attempt to connect a non-disconnected connection.";
35    return;
36  }
37
38  if (!device::BluetoothAdapterFactory::IsBluetoothAdapterAvailable()) {
39    VLOG(1)
40        << "[BC] Connection failed: Bluetooth is unsupported on this platform.";
41    return;
42  }
43
44  SetStatus(IN_PROGRESS);
45  device::BluetoothAdapterFactory::GetAdapter(
46      base::Bind(&BluetoothConnection::OnAdapterInitialized,
47                 weak_ptr_factory_.GetWeakPtr()));
48}
49
50void BluetoothConnection::Disconnect() {
51  if (status() == DISCONNECTED) {
52    VLOG(1)
53        << "[BC] Ignoring attempt to disconnect a non-connected connection.";
54    return;
55  }
56
57  // Set status as disconnected now, rather than after the socket closes, so
58  // this connection is not reused.
59  SetStatus(DISCONNECTED);
60  if (socket_.get()) {
61    socket_->Disconnect(base::Bind(&base::DoNothing));
62    socket_ = NULL;
63  }
64  if (adapter_.get()) {
65    adapter_->RemoveObserver(this);
66    adapter_ = NULL;
67  }
68}
69
70void BluetoothConnection::SendMessageImpl(scoped_ptr<WireMessage> message) {
71  DCHECK_EQ(status(), CONNECTED);
72
73  // Serialize the message.
74  std::string serialized_message = message->Serialize();
75  int message_length = base::checked_cast<int>(serialized_message.size());
76  scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(message_length);
77  memcpy(buffer->data(), serialized_message.c_str(), message_length);
78
79  // Send it.
80  pending_message_ = message.Pass();
81  base::WeakPtr<BluetoothConnection> weak_this = weak_ptr_factory_.GetWeakPtr();
82  socket_->Send(buffer,
83                message_length,
84                base::Bind(&BluetoothConnection::OnSend, weak_this),
85                base::Bind(&BluetoothConnection::OnSendError, weak_this));
86}
87
88void BluetoothConnection::DeviceRemoved(device::BluetoothAdapter* adapter,
89                                        device::BluetoothDevice* device) {
90  DCHECK_EQ(adapter, adapter_.get());
91  if (device->GetAddress() != remote_device().bluetooth_address)
92    return;
93
94  DCHECK_NE(status(), DISCONNECTED);
95  VLOG(1) << "[BC] Device disconnected...";
96  Disconnect();
97}
98
99void BluetoothConnection::ConnectToService(
100    device::BluetoothDevice* device,
101    const device::BluetoothUUID& uuid,
102    const device::BluetoothDevice::ConnectToServiceCallback& callback,
103    const device::BluetoothDevice::ConnectToServiceErrorCallback&
104        error_callback) {
105  bluetooth_util::ConnectToServiceInsecurely(
106      device, uuid, callback, error_callback);
107}
108
109void BluetoothConnection::StartReceive() {
110  base::WeakPtr<BluetoothConnection> weak_this = weak_ptr_factory_.GetWeakPtr();
111  socket_->Receive(kReceiveBufferSizeBytes,
112                   base::Bind(&BluetoothConnection::OnReceive, weak_this),
113                   base::Bind(&BluetoothConnection::OnReceiveError, weak_this));
114}
115
116void BluetoothConnection::OnAdapterInitialized(
117    scoped_refptr<device::BluetoothAdapter> adapter) {
118  const std::string address = remote_device().bluetooth_address;
119  device::BluetoothDevice* bluetooth_device = adapter->GetDevice(address);
120  if (!bluetooth_device) {
121    VLOG(1) << "[BC] Device with address " << address
122            << " is not known to the system Bluetooth daemon.";
123    Disconnect();
124    return;
125  }
126
127  adapter_ = adapter;
128  adapter_->AddObserver(this);
129
130  base::WeakPtr<BluetoothConnection> weak_this = weak_ptr_factory_.GetWeakPtr();
131  ConnectToService(
132      bluetooth_device,
133      uuid_,
134      base::Bind(&BluetoothConnection::OnConnected, weak_this),
135      base::Bind(&BluetoothConnection::OnConnectionError, weak_this));
136}
137
138void BluetoothConnection::OnConnected(
139    scoped_refptr<device::BluetoothSocket> socket) {
140  if (status() != IN_PROGRESS) {
141    // This case is reachable if the client of |this| connection called
142    // |Disconnect()| while the backing Bluetooth connection was pending.
143    DCHECK_EQ(status(), DISCONNECTED);
144    VLOG(1) << "[BC] Ignoring successful backend Bluetooth connection to an "
145            << "already disconnected logical connection.";
146    return;
147  }
148
149  VLOG(1) << "[BC] Connection established with "
150          << remote_device().bluetooth_address;
151  socket_ = socket;
152  SetStatus(CONNECTED);
153  StartReceive();
154}
155
156void BluetoothConnection::OnConnectionError(const std::string& error_message) {
157  VLOG(1) << "[BC] Connection failed: " << error_message;
158  Disconnect();
159}
160
161void BluetoothConnection::OnSend(int bytes_sent) {
162  VLOG(1) << "[BC] Successfully sent " << bytes_sent << " bytes.";
163  OnDidSendMessage(*pending_message_, true);
164  pending_message_.reset();
165}
166
167void BluetoothConnection::OnSendError(const std::string& error_message) {
168  VLOG(1) << "[BC] Error when sending bytes: " << error_message;
169  OnDidSendMessage(*pending_message_, false);
170  pending_message_.reset();
171
172  Disconnect();
173}
174
175void BluetoothConnection::OnReceive(int bytes_received,
176                                    scoped_refptr<net::IOBuffer> buffer) {
177  VLOG(1) << "[BC] Received " << bytes_received << " bytes.";
178  OnBytesReceived(std::string(buffer->data(), bytes_received));
179
180  // Post a task to delay the read until the socket is available, as
181  // calling StartReceive at this point would error with ERR_IO_PENDING.
182  base::MessageLoop::current()->PostTask(
183      FROM_HERE,
184      base::Bind(&BluetoothConnection::StartReceive,
185                 weak_ptr_factory_.GetWeakPtr()));
186}
187
188void BluetoothConnection::OnReceiveError(
189    device::BluetoothSocket::ErrorReason error_reason,
190    const std::string& error_message) {
191  VLOG(1) << "[BC] Error receiving bytes: " << error_message;
192
193  // Post a task to delay the read until the socket is available, as
194  // calling StartReceive at this point would error with ERR_IO_PENDING.
195  base::MessageLoop::current()->PostTask(
196      FROM_HERE,
197      base::Bind(&BluetoothConnection::StartReceive,
198                 weak_ptr_factory_.GetWeakPtr()));
199}
200
201}  // namespace proximity_auth
202