summaryrefslogtreecommitdiffstats
path: root/components/proximity_auth/ble/bluetooth_low_energy_connection.h
blob: 9a57608033e1984304a4473e470f8443b5cb520b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
// Copyright 2015 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.

#ifndef COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_LOW_ENERGY_CONNECTION_H
#define COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_LOW_ENERGY_CONNECTION_H

#include <queue>
#include <string>

#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "components/proximity_auth/ble/bluetooth_low_energy_characteristics_finder.h"
#include "components/proximity_auth/ble/fake_wire_message.h"
#include "components/proximity_auth/ble/remote_attribute.h"
#include "components/proximity_auth/connection.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_gatt_characteristic.h"
#include "device/bluetooth/bluetooth_gatt_notify_session.h"
#include "device/bluetooth/bluetooth_uuid.h"

namespace proximity_auth {

// Represents a connection with a remote device over Bluetooth low energy. The
// connection is a persistent bidirectional channel for sending and receiving
// wire messages. The remote device is the peripheral mode and the service
// contains two characteristics: one to send data and another to receive it.
//
// The connection flow is described below.
//
//          Discover Reader and Writer Characteristics
//                              |
//                              |
//                              |
//                    Start Notify Session
//                              |
//                              |
//                              |
//     Write kInviteToConnectSignal to Writer Characteristic
//                              |
//                              |
//                              |
//    Read kInvitationResponseSignal from Reader Characteristic
//                              |
//                              |
//                              |
//           Proximity Auth Connection Established
class BluetoothLowEnergyConnection : public Connection,
                                     public device::BluetoothAdapter::Observer {
 public:
  // Signals sent to the remote device to indicate connection related events.
  enum class ControlSignal : uint32 {
    kInviteToConnectSignal = 0,
    kInvitationResponseSignal = 1,
    kSendSignal = 2,
    kDisconnectSignal = 3,
  };

  // The sub-state of a proximity_auth::BluetoothLowEnergyConnection class
  // extends the IN_PROGRESS state of proximity_auth::Connection::Status.
  enum class SubStatus {
    DISCONNECTED,
    WAITING_GATT_CONNECTION,
    GATT_CONNECTION_ESTABLISHED,
    WAITING_CHARACTERISTICS,
    CHARACTERISTICS_FOUND,
    WAITING_NOTIFY_SESSION,
    NOTIFY_SESSION_READY,
    WAITING_RESPONSE_SIGNAL,
    CONNECTED,
  };

  // Constructs a Bluetooth low energy connection to the service with
  // |remote_service_| on the |remote_device|. The |adapter| must be already
  // initaalized and ready. The GATT connection may alreaady be established and
  // pass through |gatt_connection|. A subsequent call to Connect() must be
  // made.
  BluetoothLowEnergyConnection(
      const RemoteDevice& remote_device,
      scoped_refptr<device::BluetoothAdapter> adapter,
      const device::BluetoothUUID remote_service_uuid,
      const device::BluetoothUUID to_peripheral_char_uuid,
      const device::BluetoothUUID from_peripheral_char_uuid,
      scoped_ptr<device::BluetoothGattConnection> gatt_connection,
      int max_number_of_write_attempts);

  ~BluetoothLowEnergyConnection() override;

  // proximity_auth::Connection:
  void Connect() override;
  void Disconnect() override;

 protected:
  // Exposed for testing.
  void SetSubStatus(SubStatus status);
  SubStatus sub_status() { return sub_status_; }

  // Virtual for testing.
  virtual BluetoothLowEnergyCharacteristicsFinder* CreateCharacteristicsFinder(
      const BluetoothLowEnergyCharacteristicsFinder::SuccessCallback&
          success_callback,
      const BluetoothLowEnergyCharacteristicsFinder::ErrorCallback&
          error_callback);

  // proximity_auth::Connection:
  void SendMessageImpl(scoped_ptr<WireMessage> message) override;
  scoped_ptr<WireMessage> DeserializeWireMessage(
      bool* is_incomplete_message) override;

  // device::BluetoothAdapter::Observer:
  void DeviceChanged(device::BluetoothAdapter* adapter,
                     device::BluetoothDevice* device) override;
  void DeviceRemoved(device::BluetoothAdapter* adapter,
                     device::BluetoothDevice* device) override;
  void GattCharacteristicValueChanged(
      device::BluetoothAdapter* adapter,
      device::BluetoothGattCharacteristic* characteristic,
      const std::vector<uint8>& value) override;

 private:
  // Represents a request to write |value| to a some characteristic.
  // |is_last_write_for_wire_messsage| indicates whether this request
  // corresponds to the last write request for some wire message.
  // A WireMessage corresponds to exactly two WriteRequest: the first containing
  // a kSendSignal + the size of the WireMessage, and the second containing a
  // SendStatusSignal + the serialized WireMessage.
  struct WriteRequest {
    WriteRequest(const std::vector<uint8>& val, bool flag);
    ~WriteRequest();

    std::vector<uint8> value;
    bool is_last_write_for_wire_message;
    int number_of_failed_attempts;
  };

  // Called when the remote device is successfully disconnected.
  void OnDisconnected();

  // Called when there is an error disconnecting the remote device.
  void OnDisconnectError();

  // Called when a GATT connection is created or received by the constructor.
  void OnGattConnectionCreated(
      scoped_ptr<device::BluetoothGattConnection> gatt_connection);

  // Callback called when there is an error creating the connection.
  void OnCreateGattConnectionError(
      device::BluetoothDevice::ConnectErrorCode error_code);

  // Callback called when |to_peripheral_char_| and |from_peripheral_char_| were
  // found.
  void OnCharacteristicsFound(const RemoteAttribute& service,
                              const RemoteAttribute& to_peripheral_char,
                              const RemoteAttribute& from_peripheral_char);

  // Callback called there was an error finding the characteristics.
  void OnCharacteristicsFinderError(
      const RemoteAttribute& to_peripheral_char,
      const RemoteAttribute& from_peripheral_char);

  // Starts a notify session for |from_peripheral_char_| when ready
  // (SubStatus::CHARACTERISTICS_FOUND).
  void StartNotifySession();

  // Called when a notification session is successfully started for
  // |from_peripheral_char_| characteristic.
  void OnNotifySessionStarted(
      scoped_ptr<device::BluetoothGattNotifySession> notify_session);

  // Called when there is an error starting a notification session for
  // |from_peripheral_char_| characteristic.
  void OnNotifySessionError(device::BluetoothGattService::GattErrorCode);

  // Stops |notify_session_|.
  void StopNotifySession();

  // Sends an invite to connect signal to the peripheral if when ready
  // (SubStatus::NOTIFY_SESSION_READY).
  void SendInviteToConnectSignal();

  // Completes and updates the status accordingly.
  void CompleteConnection();

  // This is the only entry point for WriteRequests, which are processed
  // accordingly the following flow:
  // 1) |request| is enqueued;
  // 2) |request| will be processed by ProcessNextWriteRequest() when there is
  // no pending write request;
  // 3) |request| will be dequeued when it's successfully processed
  // (OnRemoteCharacteristicWritten());
  // 4) |request| is not dequeued if it fails
  // (OnWriteRemoteCharacteristicError()), it remains on the queue and will be
  // retried. |request| will remain on the queue until it succeeds or it
  // triggers a Disconnect() call (after |max_number_of_tries_|).
  void WriteRemoteCharacteristic(WriteRequest request);

  // Processes the next request in |write_requests_queue_|.
  void ProcessNextWriteRequest();

  // Called when the BluetoothGattCharacteristic::RemoteCharacteristicWrite() is
  // sucessfully complete.
  void OnRemoteCharacteristicWritten(bool run_did_send_message_callback);

  // Called when there is an error writing to the remote characteristic
  // |to_peripheral_char_|.
  void OnWriteRemoteCharacteristicError(
      bool run_did_send_message_callback,
      device::BluetoothGattService::GattErrorCode error);

  // Builds the value to be written on |to_peripheral_char_|. The value
  // corresponds to |signal| concatenated with |payload|.
  WriteRequest BuildWriteRequest(const std::vector<uint8>& signal,
                                 const std::vector<uint8>& bytes,
                                 bool is_last_message_for_wire_message);

  // Clears |write_requests_queue_|.
  void ClearWriteRequestsQueue();

  // Returns the Bluetooth address of the remote device.
  const std::string& GetRemoteDeviceAddress();

  // Returns the device corresponding to |remote_device_address_|.
  device::BluetoothDevice* GetRemoteDevice();

  // Returns the service corresponding to |remote_service_| in the current
  // device.
  device::BluetoothGattService* GetRemoteService();

  // Returns the characteristic corresponding to |identifier| in the current
  // service.
  device::BluetoothGattCharacteristic* GetGattCharacteristic(
      const std::string& identifier);

  // Convert the first 4 bytes from a byte vector to a uint32.
  uint32 ToUint32(const std::vector<uint8>& bytes);

  // Convert an uint32 to a byte vector.
  const std::vector<uint8> ToByteVector(uint32 value);

  // The Bluetooth adapter over which the Bluetooth connection will be made.
  scoped_refptr<device::BluetoothAdapter> adapter_;

  // Remote service the |gatt_connection_| was established with.
  RemoteAttribute remote_service_;

  // Characteristic used to send data to the remote device.
  RemoteAttribute to_peripheral_char_;

  // Characteristic used to receive data from the remote device.
  RemoteAttribute from_peripheral_char_;

  // The GATT connection with the remote device.
  scoped_ptr<device::BluetoothGattConnection> gatt_connection_;

  // The characteristics finder for remote device.
  scoped_ptr<BluetoothLowEnergyCharacteristicsFinder> characteristic_finder_;

  // The notify session for |from_peripheral_char|.
  scoped_ptr<device::BluetoothGattNotifySession> notify_session_;

  // Internal connection status
  SubStatus sub_status_;

  // Indicates a receiving operation is in progress. This is set after a
  // ControlSignal::kSendSignal was received from the remote device.
  bool receiving_bytes_;

  // Total number of bytes expected for the current receive operation.
  std::size_t expected_number_of_incoming_bytes_;

  // Bytes already received for the current receive operation.
  std::string incoming_bytes_buffer_;

  // Indicates there is a BluetoothGattCharacteristic::WriteRemoteCharacteristic
  // operation pending.
  bool write_remote_characteristic_pending_;

  std::queue<WriteRequest> write_requests_queue_;

  // Maximum number of tries to send any write request.
  int max_number_of_write_attempts_;

  // Maximum number of bytes that fit in a single chunk to be written in
  // |to_peripheral_char_|. Ideally, this should be the maximum value the
  // peripheral supports and it should be agreed when the GATT connection is
  // created. Currently, there is no API to find this value. The implementation
  // uses a hard-coded constant.
  int max_chunk_size_;

  // Stores when the instace was created.
  base::TimeTicks start_time_;

  base::WeakPtrFactory<BluetoothLowEnergyConnection> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(BluetoothLowEnergyConnection);
};

}  // namespace proximity_auth

#endif  // COMPONENTS_PROXIMITY_AUTH_BLUETOOTH_LOW_ENERGY_CONNECTION_H