| // Copyright 2018 The Chromium OS 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 BLUETOOTH_NEWBLUED_NEWBLUE_H_ |
| #define BLUETOOTH_NEWBLUED_NEWBLUE_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <base/callback.h> |
| #include <base/location.h> |
| #include <base/memory/ref_counted.h> |
| #include <base/memory/weak_ptr.h> |
| #include <base/single_thread_task_runner.h> |
| #include <newblue/att.h> |
| #include <newblue/gatt.h> |
| |
| #include "bluetooth/newblued/gatt_attributes.h" |
| #include "bluetooth/newblued/libnewblue.h" |
| #include "bluetooth/newblued/property.h" |
| #include "bluetooth/newblued/uuid.h" |
| |
| namespace bluetooth { |
| |
| using UniqueId = uint64_t; |
| constexpr UniqueId kInvalidUniqueId = 0; |
| |
| constexpr char kAdapterObjectPath[] = "/org/bluez/hci0"; |
| |
| // The value(s) is/are based on libnewblue API. |
| constexpr gatt_client_conn_t kInvalidGattConnectionId = 0; |
| |
| // These are based on the numbers assigned by Bluetooth SIG, see |
| // www.bluetooth.com/specifications/assigned-numbers/generic-access-profile. |
| // Note that only the subset of all EIR data types is needed for now. |
| enum class EirType : uint8_t { |
| FLAGS = 0x01, |
| UUID16_INCOMPLETE = 0x02, |
| UUID16_COMPLETE = 0x03, |
| UUID32_INCOMPLETE = 0x04, |
| UUID32_COMPLETE = 0x05, |
| UUID128_INCOMPLETE = 0x06, |
| UUID128_COMPLETE = 0x07, |
| NAME_SHORT = 0x08, |
| NAME_COMPLETE = 0x09, |
| TX_POWER = 0x0a, |
| CLASS_OF_DEV = 0x0d, |
| SVC_DATA16 = 0x16, |
| GAP_APPEARANCE = 0x19, |
| SVC_DATA32 = 0x20, |
| SVC_DATA128 = 0x21, |
| MANUFACTURER_DATA = 0xff, |
| }; |
| |
| // These are based on the pairing state defined in newblue/sm.h. |
| enum class PairState : uint8_t { |
| NOT_PAIRED, |
| STARTED, |
| PAIRED, |
| CANCELED, |
| FAILED, |
| }; |
| |
| // These are based on the pairing errors defined in newblue/sm.h. |
| enum class PairError : uint8_t { |
| NONE, |
| ALREADY_PAIRED, |
| IN_PROGRESS, |
| INVALID_PAIR_REQ, |
| PASSKEY_FAILED, |
| OOB_NOT_AVAILABLE, |
| AUTH_REQ_INFEASIBLE, |
| CONF_VALUE_MISMATCHED, |
| PAIRING_NOT_SUPPORTED, |
| ENCR_KEY_SIZE, |
| REPEATED_ATTEMPT, |
| INVALID_PARAM, |
| MEMORY, |
| L2C_CONN, |
| NO_SUCH_DEVICE, |
| UNEXPECTED_SM_CMD, |
| SEND_SM_CMD, |
| ENCR_CONN, |
| UNEXPECTED_L2C_EVT, |
| STALLED, |
| UNKNOWN |
| }; |
| |
| // These are based on GATT_CLI_STATUS_* in newblue/gatt.h |
| enum class GattClientOperationStatus : uint8_t { |
| OK, |
| OTHER_SIDE_DISC, |
| ERR, |
| WE_DISC |
| }; |
| |
| // These are based on ATT_ERROR_* in newblue/att.h. |
| enum class AttError : uint8_t { |
| NONE = ATT_ERROR_NONE, |
| INVALID_HANDLE = ATT_ERROR_INVALID_HANDLE, |
| READ_NOT_ALLOWED = ATT_ERROR_READ_NOT_ALLOWED, |
| WRITE_NOT_ALLOWED = ATT_ERROR_WRITE_NOT_ALLOWED, |
| INVALID_PDU = ATT_ERROR_INVALID_PDU, |
| INSUFF_AUTHN = ATT_ERROR_INSUFFICIENT_AUTH, |
| REQ_NOT_SUPPORTED = ATT_ERROR_REQ_NOT_SUPPORTED, |
| INVALID_OFFSET = ATT_ERROR_INVALID_OFFSET, |
| INSUFF_AUTHZ = ATT_ERROR_INSUFFICIENT_ATHOR, |
| PREPARE_QUEUE_FULL = ATT_ERROR_PREPARE_QUEUE_FULL, |
| ATTR_NOT_FOUND = ATT_ERROR_ATTRIBUTE_NOT_FOUND, |
| ATTR_NOT_LONG = ATT_ERROR_ATTRIBUTE_NOT_LONG, |
| INSUFF_ENCR_KEY_SIZE = ATT_ERROR_INSUFF_ENCR_KEY_SZ, |
| INVALID_ATTR_VALUE_LENGTH = ATT_ERROR_INVAL_ATTR_VAL_LEN, |
| UNLIKELY_ERROR = ATT_ERROR_UNLIKELY_ERROR, |
| INSUFF_ENCR = ATT_ERROR_INSUFFICIENT_ENCR, |
| UNSUPPORTED_GROUP_TYPE = ATT_ERROR_UNSUPP_GROUP_TYPE, |
| INSUFF_RESOURCES = ATT_ERROR_INSUFFICIENT_RSRCS, |
| APP_ERROR_FIRST = ATT_ERROR_APP_ERR_FIRST, |
| APP_ERROR_LAST = ATT_ERROR_APP_ERR_LAST, |
| COMMON_FIRST = ATT_ERROR_COMMON_FIRST, |
| COMMON_LAST = ATT_ERROR_COMMON_LAST, |
| }; |
| |
| // TODO(mcchou): Add more entries for GATT client operations. |
| // The is based on gattCli*Cbk defined in newblue/gatt.h. |
| enum class GattClientOperationType { |
| SERVICES_ENUM, |
| PRIMARY_SERVICE_TRAV, |
| READ_LONG_VALUE, |
| }; |
| |
| // These are based on GATT_CLI_AUTH_REQ_* defined in newblue/gatt.h. |
| enum class GattClientOperationAuthentication : uint8_t { |
| NONE, |
| AUTHN_NO_MITM, |
| AUTHN_MITM, |
| AUTHN_SIGNED_NO_MITM, |
| AUTHN_SIGNED_MITM, |
| }; |
| |
| // Agent to receive pairing user interaction events. |
| class PairingAgent { |
| public: |
| virtual void DisplayPasskey(const std::string& device_address, |
| uint32_t passkey) = 0; |
| // Get the user's authorization to continue the pairing process. |
| virtual void RequestAuthorization(const std::string& device_address) = 0; |
| // TODO(sonnysasaka): Add other methods: |
| // RequestPinCode, DisplayPinCode, RequestPasskey, RequestConfirmation, |
| // AuthorizeService. |
| }; |
| |
| // Structure representing a known device. |
| struct KnownDevice { |
| // The known device name. |
| std::string name; |
| // MAC address (in format XX:XX:XX:XX:XX:XX). |
| std::string address; |
| // libnewblue's BT_ADDR_TYPE_* |
| uint8_t address_type; |
| // Whether the device was previously paired. |
| bool is_paired; |
| // Identity address (in format XX:XX:XX:XX:XX:XX). |
| std::string identity_address; |
| }; |
| |
| // A higher-level API wrapper of the low-level libnewblue C interface. |
| // This class abstracts away the C interface details of libnewblue and provides |
| // event handling model that is compatible with libchrome's main loop. |
| class Newblue { |
| public: |
| using DeviceDiscoveredCallback = |
| base::Callback<void(const std::string& adv_address, |
| uint8_t address_type, |
| const std::string& resolved_address, |
| int8_t rssi, |
| uint8_t reply_type, |
| const std::vector<uint8_t>& eir)>; |
| |
| using PairStateChangedCallback = |
| base::Callback<void(const std::string& address, |
| PairState pair_state, |
| PairError pair_error, |
| const std::string& identity_address)>; |
| |
| using GattClientConnectCallback = |
| base::Callback<void(gatt_client_conn_t conn_id, uint8_t status)>; |
| |
| using GattClientServicesEnumCallback = |
| base::Callback<void(bool finished, |
| gatt_client_conn_t conn_id, |
| UniqueId transaction_id, |
| Uuid uuid, |
| bool primary, |
| uint16_t first_handle, |
| uint16_t num_handles, |
| GattClientOperationStatus status)>; |
| |
| using GattClientPrimaryServiceTravCallback = |
| base::Callback<void(gatt_client_conn_t conn_id, |
| UniqueId transaction_id, |
| std::unique_ptr<GattService> service)>; |
| |
| // Empty |value| indicates error. If |value| is empty, |error| should be |
| // any AttError code other than AttError::NONE. |
| using GattClientReadLongValueCallback = |
| base::Callback<void(gatt_client_conn_t conn, |
| UniqueId transaction_id, |
| uint16_t value_handle, |
| AttError error, |
| const std::vector<uint8_t>& value)>; |
| |
| // Represents a GATT client operation. |
| struct GattClientOperation { |
| GattClientOperationType type; |
| GattClientServicesEnumCallback services_enum_callback; |
| GattClientPrimaryServiceTravCallback service_trav_callback; |
| GattClientReadLongValueCallback read_long_value_callback; |
| }; |
| |
| explicit Newblue(std::unique_ptr<LibNewblue> libnewblue); |
| |
| ~Newblue() = default; |
| |
| LibNewblue* libnewblue() { return libnewblue_.get(); } |
| |
| base::WeakPtr<Newblue> GetWeakPtr(); |
| |
| // Initializes the LE stack (blocking call). |
| // Returns true if initialization succeeds, false otherwise. |
| bool Init(); |
| |
| // Registers/Unregisters pairing agent which handles user interactions during |
| // pairing process. |
| // |pairing_agent| not owned, must outlive this object. |
| void RegisterPairingAgent(PairingAgent* pairing_agent); |
| void UnregisterPairingAgent(); |
| |
| // Listens to reset complete event from the chip. This is useful to detect |
| // when NewBlue is ready to bring up the stack. |
| // Returns true on success and false otherwise. |
| bool ListenReadyForUp(base::Closure callback); |
| |
| // Brings up the NewBlue stack. This should be called when the adapter has |
| // just been turned on, detected when there is reset complete event from the |
| // chip. ListenReadyForUp() should be used to detect this event. |
| // Returns true if success, false otherwise. |
| bool BringUp(); |
| |
| // Starts LE scanning, |callback| will be called every time inquiry response |
| // is received. |
| bool StartDiscovery(bool active, |
| uint16_t scan_interval, |
| uint16_t scan_window, |
| bool use_random_addr, |
| bool only_whitelist, |
| bool filter_duplicates, |
| DeviceDiscoveredCallback callback); |
| // Stops LE scanning. |
| bool StopDiscovery(); |
| |
| // Registers as an observer of pairing states of devices. |
| UniqueId RegisterAsPairObserver(PairStateChangedCallback callback); |
| // Unregisters as an observer of pairing states. |
| void UnregisterAsPairObserver(UniqueId observer_id); |
| |
| // Performs LE pairing. |
| bool Pair(const std::string& device_address, |
| bool is_random_address, |
| smPairSecurityRequirements security_requirement); |
| // Cancels LE pairing. |
| bool CancelPair(const std::string& device_address, bool is_random_address); |
| |
| // Registers/Unregisters a callback for GATT connection state change |
| // notifications. We allow only one observer for connection callback. |
| bool RegisterGattClientConnectCallback(GattClientConnectCallback callback); |
| void UnregisterGattClientConnectCallback(); |
| // Connects as a GATT client to a peer device. |
| gatt_client_conn_t GattClientConnect(const std::string& device_address, |
| bool is_random_address); |
| // Disconnects from a peer device. |
| GattClientOperationStatus GattClientDisconnect(gatt_client_conn_t conn_id); |
| |
| // Retrieves the known devices from NewBlue's persist. |
| std::vector<KnownDevice> GetKnownDevices(); |
| |
| // Enumerates GATT services provided by a connected device. |
| GattClientOperationStatus GattClientEnumServices( |
| gatt_client_conn_t conn_id, |
| bool primary, |
| UniqueId transaction_id, |
| GattClientServicesEnumCallback callback); |
| |
| // Traverses the attributes included in a primary service. |
| GattClientOperationStatus GattClientTravPrimaryService( |
| gatt_client_conn_t conn_id, |
| const Uuid& uuid, |
| UniqueId transaction_id, |
| GattClientPrimaryServiceTravCallback callback); |
| |
| // Reads the complete value of a GATT characteristic or a GATT descriptor. |
| GattClientOperationStatus GattClientReadLongValue( |
| gatt_client_conn_t conn_id, |
| uint16_t value_handle, |
| GattClientOperationAuthentication authentication, |
| UniqueId transaction_id, |
| GattClientReadLongValueCallback callback); |
| |
| private: |
| // Posts task to the thread which created this Newblue object. |
| // libnewblue callbacks should always post task using this method rather |
| // than doing any processing in the callback's thread. |
| bool PostTask(const base::Location& from_here, const base::Closure& task); |
| |
| // Called by NewBlue when it's ready to bring up the stack. This is called |
| // on one of NewBlue's threads, so we shouldn't do anything on this thread |
| // other than posting the task to our mainloop thread. |
| static void OnStackReadyForUpThunk(void* data); |
| // Triggers the callback registered via ListenReadyForUp(). |
| void OnStackReadyForUp(); |
| |
| static void DiscoveryCallbackThunk(void* data, |
| const struct bt_addr* adv_address, |
| const struct bt_addr* resolved_address, |
| int8_t rssi, |
| uint8_t reply_type, |
| const void* eir, |
| uint8_t eir_len); |
| // Called when inquiry response is received as a result of StartDiscovery(). |
| void DiscoveryCallback(const std::string& adv_address, |
| uint8_t address_type, |
| const std::string& resolved_address, |
| int8_t rssi, |
| uint8_t reply_type, |
| const std::vector<uint8_t>& eir); |
| |
| static void PairStateCallbackThunk(void* data, |
| const void* pair_state_change, |
| uniq_t observer_id); |
| // Called when pairing state changed events are received. |
| void PairStateCallback(const smPairStateChange& change, uniq_t observer_id); |
| |
| static void GattConnectCallbackThunk(void* user_data, |
| gatt_client_conn_t conn, |
| uint8_t status); |
| |
| static void GattClientEnumServicesCallbackThunk(void* user_data, |
| gatt_client_conn_t conn_id, |
| uniq_t transaction_id, |
| const struct uuid* uuid, |
| bool primary, |
| uint16_t first_handle, |
| uint16_t num_handles, |
| uint8_t status); |
| // Called when the service enumeration results are received. |
| void GattClientEnumServicesCallback(gatt_client_conn_t conn_id, |
| uniq_t transaction_id, |
| Uuid uuid, |
| bool primary, |
| uint16_t first_handle, |
| uint16_t num_handles, |
| uint8_t status); |
| |
| static void GattClientTravPrimaryServiceCallbackThunk( |
| void* user_data, |
| gatt_client_conn_t conn, |
| uniq_t transaction_id, |
| const struct GattTraversedService* service); |
| // Called when the primary service traversal result is received. |
| void GattClientTravPrimaryServiceCallback( |
| gatt_client_conn_t conn_id, |
| uniq_t transaction_id, |
| std::unique_ptr<GattService> service); |
| |
| static void GattClientReadLongCallbackThunk(void* user_data, |
| gatt_client_conn_t conn, |
| uniq_t transaction_id, |
| uint16_t handle, |
| uint8_t error, |
| sg data); |
| // Called when the result of reading long value is received. |
| void GattClientReadLongCallback(gatt_client_conn_t conn, |
| uniq_t transaction_id, |
| uint16_t handle, |
| AttError error, |
| std::vector<uint8_t> value); |
| |
| static void PasskeyDisplayObserverCallbackThunk( |
| void* data, |
| const struct smPasskeyDisplay* passkey_display, |
| uniq_t observer_id); |
| void PasskeyDisplayObserverCallback(struct smPasskeyDisplay passkey_display, |
| uniq_t observer_id); |
| |
| std::unique_ptr<LibNewblue> libnewblue_; |
| |
| scoped_refptr<base::SingleThreadTaskRunner> origin_task_runner_; |
| |
| base::Closure ready_for_up_callback_; |
| |
| DeviceDiscoveredCallback device_discovered_callback_; |
| uniq_t discovery_handle_ = 0; |
| |
| uniq_t passkey_display_observer_id_ = 0; |
| |
| PairingAgent* pairing_agent_ = nullptr; |
| |
| // Handle from security manager of being a central pairing observer. We are |
| // responsible of receiving pairing state changed events and informing |
| // clients whoever registered as pairing observers. |
| uniq_t pair_state_handle_; |
| // Contains pairs of <observer ID, callback>. Clients who registered for the |
| // pairing update can expect to be notified via callback provided in |
| // RegisterAsPairObserver(). For now, Newblued is the only client. |
| std::map<UniqueId, PairStateChangedCallback> pair_observers_; |
| |
| GattClientConnectCallback gatt_client_connect_callback_; |
| |
| // Contains pairs of <transaction ID, GATT operation> which track the ongoing |
| // GATT client operations. |
| std::map<UniqueId, GattClientOperation> gatt_client_ops_; |
| |
| // Must come last so that weak pointers will be invalidated before other |
| // members are destroyed. |
| base::WeakPtrFactory<Newblue> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Newblue); |
| }; |
| |
| } // namespace bluetooth |
| |
| #endif // BLUETOOTH_NEWBLUED_NEWBLUE_H_ |