| // 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 base { |
| class TaskRunner; |
| } |
| |
| namespace proximity_auth { |
| |
| class BluetoothThrottler; |
| |
| // 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, |
| 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, |
| BluetoothThrottler* bluetooth_throttler, |
| int max_number_of_write_attempts); |
| |
| ~BluetoothLowEnergyConnection() override; |
| |
| // proximity_auth::Connection: |
| void Connect() override; |
| void Disconnect() override; |
| std::string GetDeviceAddress() override; |
| |
| protected: |
| // Exposed for testing. |
| void SetSubStatus(SubStatus status); |
| SubStatus sub_status() { return sub_status_; } |
| |
| // Sets |task_runner_| for testing. |
| void SetTaskRunnerForTesting(scoped_refptr<base::TaskRunner> task_runner); |
| |
| // 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; |
| |
| // 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; |
| }; |
| |
| // Creates the GATT connection with |remote_device|. |
| void CreateGattConnection(); |
| |
| // Called when a GATT connection is created. |
| void OnGattConnectionCreated( |
| scoped_ptr<device::BluetoothGattConnection> gatt_connection); |
| |
| // Callback called when there is an error creating the GATT 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); |
| |
| // Prints the time elapsed since |Connect()| was called. |
| void PrintTimeElapsed(); |
| |
| // 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_; |
| |
| // Throttles repeated connection attempts to the same device. This is a |
| // workaround for crbug.com/508919. Not owned, must outlive this instance. |
| BluetoothThrottler* bluetooth_throttler_; |
| |
| scoped_refptr<base::TaskRunner> task_runner_; |
| |
| // 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 |