diff options
author | Sven Gothel <[email protected]> | 2021-01-25 18:44:32 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2021-01-25 18:44:32 +0100 |
commit | e7d892210343dc50c8978954f8daa71395584f2e (patch) | |
tree | 4d5f1dfcede30af5cad2a7afb97b535f40600a85 /api/direct_bt/BTDevice.hpp | |
parent | feaff07f5942ee6ff2bd94e70bf140c678379490 (diff) |
Java: New API Layout: Shorten named [Bluetooth -> BT], [Characteristic -> Char] etc (align naming with direct_bt == java; reduce java code footprint and too long code lines)
Diffstat (limited to 'api/direct_bt/BTDevice.hpp')
-rw-r--r-- | api/direct_bt/BTDevice.hpp | 831 |
1 files changed, 831 insertions, 0 deletions
diff --git a/api/direct_bt/BTDevice.hpp b/api/direct_bt/BTDevice.hpp new file mode 100644 index 00000000..a2f529d6 --- /dev/null +++ b/api/direct_bt/BTDevice.hpp @@ -0,0 +1,831 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef BT_DEVICE_HPP_ +#define BT_DEVICE_HPP_ + +#include <cstring> +#include <string> +#include <memory> +#include <cstdint> + +#include <mutex> + +#include <jau/darray.hpp> + +#include "BTTypes1.hpp" + +#include "HCIIoctl.hpp" +#include "HCIComm.hpp" + +#include "MgmtTypes.hpp" +#include "SMPHandler.hpp" +#include "BTGattHandler.hpp" + +namespace direct_bt { + + // ************************************************* + // ************************************************* + // ************************************************* + + class BTAdapter; // forward + + class BTDevice : public BTObject + { + friend BTAdapter; // managing us: ctor and update(..) during discovery + friend BTGattHandler; // may issue detailed disconnect(..) + + private: + BTAdapter & adapter; + L2CAPComm l2cap_att; + uint64_t ts_last_discovery; + uint64_t ts_last_update; + std::string name; + int8_t rssi = 127; // The core spec defines 127 as the "not available" value + int8_t tx_power = 127; // The core spec defines 127 as the "not available" value + AppearanceCat appearance = AppearanceCat::UNKNOWN; + jau::relaxed_atomic_uint16 hciConnHandle; + jau::ordered_atomic<LEFeatures, std::memory_order_relaxed> le_features; + std::shared_ptr<ManufactureSpecificData> advMSD = nullptr; + jau::darray<std::shared_ptr<uuid_t>> advServices; +#if SMP_SUPPORTED_BY_OS + std::shared_ptr<SMPHandler> smpHandler = nullptr; + std::recursive_mutex mtx_smpHandler; +#endif + std::shared_ptr<BTGattHandler> gattHandler = nullptr; + std::recursive_mutex mtx_gattHandler; + std::recursive_mutex mtx_connect; + std::recursive_mutex mtx_data; + std::atomic<bool> isConnected; + std::atomic<bool> allowDisconnect; // allowDisconnect = isConnected || 'isConnectIssued' + + struct PairingData { + SMPIOCapability ioCap_conn=SMPIOCapability::UNSET; + SMPIOCapability ioCap_user=SMPIOCapability::UNSET; + BTSecurityLevel sec_level_conn=BTSecurityLevel::UNSET; + BTSecurityLevel sec_level_user=BTSecurityLevel::UNSET; + + SMPPairingState state; + PairingMode mode; + bool res_requested_sec; + bool use_sc; + + SMPAuthReqs authReqs_init, authReqs_resp; + SMPIOCapability ioCap_init, ioCap_resp; + SMPOOBDataFlag oobFlag_init, oobFlag_resp; + uint8_t maxEncsz_init, maxEncsz_resp; + SMPKeyType keys_init_exp, keys_resp_exp; + SMPKeyType keys_init_has, keys_resp_has; + + // LTK: Set of Long Term Key data: ltk, ediv + rand + SMPLongTermKeyInfo ltk_init, ltk_resp; + + // IRK + jau::uint128_t irk_init, irk_resp; + EUI48 address; + bool is_static_random_address; + + // CSRK + SMPSignatureResolvingKeyInfo csrk_init, csrk_resp; + }; + PairingData pairing_data; + std::mutex mtx_pairing; + jau::sc_atomic_bool sync_pairing; + + /** Private class only for private make_shared(). */ + class ctor_cookie { friend BTDevice; ctor_cookie(const uint16_t secret) { (void)secret; } }; + + /** Private std::make_shared<BTDevice>(..) vehicle for friends. */ + static std::shared_ptr<BTDevice> make_shared(BTAdapter & adapter, EInfoReport const & r) { + return std::make_shared<BTDevice>(BTDevice::ctor_cookie(0), adapter, r); + } + + /** Add advertised service (GAP discovery) */ + bool addAdvService(std::shared_ptr<uuid_t> const &uuid) noexcept; + /** Add advertised service (GAP discovery) */ + bool addAdvServices(jau::darray<std::shared_ptr<uuid_t>> const & services) noexcept; + /** + * Find advertised service (GAP discovery) index + * @return index >= 0 if found, otherwise -1 + */ + int findAdvService(std::shared_ptr<uuid_t> const &uuid) const noexcept; + + EIRDataType update(EInfoReport const & data) noexcept; + EIRDataType update(GattGenericAccessSvc const &data, const uint64_t timestamp) noexcept; + + void notifyDisconnected() noexcept; + void notifyConnected(std::shared_ptr<BTDevice> sthis, const uint16_t handle, const SMPIOCapability io_cap) noexcept; + void notifyLEFeatures(std::shared_ptr<BTDevice> sthis, const LEFeatures features) noexcept; + + /** + * Setup L2CAP channel connection to device incl. optional security encryption level off-thread. + * <p> + * Will be performed after connectLE(..), i.e. notifyConnected() and notifyLEFeatures(), + * initiated by the latter. + * </p> + */ + void processL2CAPSetup(std::shared_ptr<BTDevice> sthis); + + /** + * Established SMP host connection and security for L2CAP connection if sec_level > BTSecurityLevel::NONE. + * <p> + * Will be performed after connectLE(..), i.e. notifyConnected() and notifyLEFeatures().<br> + * Called from processL2CAPSetup, if supported. + * </p> + * <p> + * If sec_level > BTSecurityLevel::NONE, sets the BlueZ's L2CAP socket BT_SECURITY sec_level, determining the SMP security mode per connection. + * </p> + * <p> + * The SMPHandler is managed by this device instance and closed via disconnectSMP(). + * </p> + * + * @param sec_level sec_level <= BTSecurityLevel::NONE will not set security level and returns false. + * @return true if a security level > BTSecurityLevel::NONE has been set successfully, false if no security level has been set or if it failed. + */ + bool connectSMP(std::shared_ptr<BTDevice> sthis, const BTSecurityLevel sec_level) noexcept; + + bool checkPairingKeyDistributionComplete(const std::string& timestamp) const noexcept; + + bool updatePairingState(std::shared_ptr<BTDevice> sthis, const MgmtEvent& evt, const HCIStatusCode evtStatus, SMPPairingState claimed_state) noexcept; + + /** + * Forwarded from HCIHandler -> BTAdapter -> this BTDevice + * <p> + * Will be initiated by processL2CAPSetup()'s security_level setup after connectLE(..), i.e. notifyConnected() and notifyLEFeatures(). + * </p> + */ + void hciSMPMsgCallback(std::shared_ptr<BTDevice> sthis, const SMPPDUMsg& msg, const HCIACLData::l2cap_frame& source) noexcept; + + /** + * Setup GATT via connectGATT() off-thread. + * <p> + * <p> + * Will be performed after connectLE(..), i.e. notifyConnected() and notifyLEFeatures().<br> + * Called from either processL2CAPSetup() w/o security or with SMP security readiness from hciSMPMsgCallback(). + * </p> + */ + void processDeviceReady(std::shared_ptr<BTDevice> sthis, const uint64_t timestamp); + + /** + * Returns a newly established GATT connection. + * <p> + * Will be performed after connectLE(..) via notifyConnected(), processNotifyConnectedOffThread(). + * </p> + * <p> + * The GATTHandler is managed by this device instance and closed via disconnectGATT(). + * </p> + */ + bool connectGATT(std::shared_ptr<BTDevice> sthis) noexcept; + + /** + * Will be performed within disconnect() and notifyDisconnected(). + */ + void disconnectGATT(const int caller) noexcept; + + /** + * Will be performed within disconnect() and notifyDisconnected(). + */ + void disconnectSMP(const int caller) noexcept; + + void clearSMPStates(const bool connected) noexcept; + + void sendMgmtEvDeviceDisconnected(std::unique_ptr<MgmtEvent> evt) noexcept; + + public: + const uint64_t ts_creation; + /** Device's unique mac address and type tuple. */ + const BDAddressAndType addressAndType; // FIXME: Mutable for resolvable -> identity during pairing? + + /** Private ctor for private BTDevice::make_shared() intended for friends. */ + BTDevice(const BTDevice::ctor_cookie& cc, BTAdapter & adapter, EInfoReport const & r); + + BTDevice(const BTDevice&) = delete; + void operator=(const BTDevice&) = delete; + + /** + * Releases this instance after calling {@link #remove()}. + */ + ~BTDevice() noexcept; + + std::string get_java_class() const noexcept override { + return java_class(); + } + static std::string java_class() noexcept { + return std::string(JAVA_DBT_PACKAGE "DBTDevice"); + } + + /** Returns the managing adapter */ + BTAdapter & getAdapter() const { return adapter; } + + /** Returns the shared pointer of this instance managed by the adapter. */ + std::shared_ptr<BTDevice> getSharedInstance() const noexcept; + + /** + * Returns the timestamp in monotonic milliseconds when this device instance has been created, + * either via its initial discovery or its initial direct connection. + * @see BasicTypes::getCurrentMilliseconds() + */ + uint64_t getCreationTimestamp() const noexcept { return ts_creation; } + + /** + * Returns the timestamp in monotonic milliseconds when this device instance has + * discovered or connected directly the last time. + * @see BasicTypes::getCurrentMilliseconds() + */ + uint64_t getLastDiscoveryTimestamp() const noexcept { return ts_last_discovery; } + + /** + * Returns the timestamp in monotonic milliseconds when this device instance underlying data + * has been updated the last time. + * @see BasicTypes::getCurrentMilliseconds() + */ + uint64_t getLastUpdateTimestamp() const noexcept { return ts_last_update; } + + /** + * @see getLastUpdateTimestamp() + */ + uint64_t getLastUpdateAge(const uint64_t ts_now) const noexcept { return ts_now - ts_last_update; } + + /** + * Returns the unique device EUI48 address and BDAddressType type. + * @since 2.2.0 + */ + constexpr BDAddressAndType const & getAddressAndType() const noexcept { return addressAndType; } + + /** Return RSSI of device as recognized at discovery and connect. */ + int8_t getRSSI() const noexcept { return rssi; } + + /** Return Tx Power of device as recognized at discovery and connect. */ + int8_t getTxPower() const noexcept { return tx_power; } + + /** Return AppearanceCat of device as recognized at discovery, connect and GATT discovery. */ + AppearanceCat getAppearance() const noexcept { return appearance; } + + std::string const getName() const noexcept; + + /** Return shared ManufactureSpecificData as recognized at discovery, pre GATT discovery. */ + std::shared_ptr<ManufactureSpecificData> const getManufactureSpecificData() const noexcept; + + /** + * Return a list of advertised services as recognized at discovery, pre GATT discovery. + * <p> + * To receive a complete list of GATT services including characteristics etc, + * use {@link #getGattService()}. + * </p> + */ + jau::darray<std::shared_ptr<uuid_t>> getAdvertisedServices() const noexcept; + + std::string toString() const noexcept override { return toString(false); } + + std::string toString(bool includeDiscoveredServices) const noexcept; + + /** + * Retrieves the current connection info for this device and returns the ConnectionInfo reference if successful, + * otherwise returns nullptr. + * <p> + * Before this method returns, the internal rssi and tx_power will be updated if any changed + * and therefore all BTAdapterStatusListener's deviceUpdated(..) method called for notification. + * </p> + */ + std::shared_ptr<ConnectionInfo> getConnectionInfo() noexcept; + + /** + * Return true if the device has been successfully connected, otherwise false. + */ + bool getConnected() noexcept { return isConnected.load(); } + + /** + * Establish a HCI BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM connection to this device. + * <p> + * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command + * </p> + * <p> + * If this device's addressType is not BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM, + * HCIStatusCode::UNACCEPTABLE_CONNECTION_PARAM is being returned. + * </p> + * <p> + * The actual new connection handle will be delivered asynchronous and + * the connection event can be caught via AdapterStatusListener::deviceConnected(..), + * or if failed via AdapterStatusListener::deviceDisconnected(..). + * </p> + * <p> + * The device is tracked by the managing adapter. + * </p> + * <p> + * Default parameter values are chosen for using public address resolution + * and usual connection latency, interval etc. + * </p> + * <p> + * Set window to the same value as the interval, enables continuous scanning. + * </p> + * <p> + * The associated BTAdapter's HCIHandler instance is used to connect, + * see HCIHandler::le_create_conn(). + * </p> + * + * @param le_scan_interval in units of 0.625ms, default value 24 for 15ms; Value range [4 .. 0x4000] for [2.5ms .. 10.24s] + * @param le_scan_window in units of 0.625ms, default value 24 for 15ms; Value range [4 .. 0x4000] for [2.5ms .. 10.24s]. Shall be <= le_scan_interval + * @param conn_interval_min in units of 1.25ms, default value 12 for 15ms; Value range [6 .. 3200] for [7.5ms .. 4000ms] + * @param conn_interval_max in units of 1.25ms, default value 12 for 15ms; Value range [6 .. 3200] for [7.5ms .. 4000ms] + * @param conn_latency slave latency in units of connection events, default value 0; Value range [0 .. 0x01F3]. + * @param supervision_timeout in units of 10ms, default value >= 10 x conn_interval_max, we use HCIConstInt::LE_CONN_MIN_TIMEOUT_MS minimum; Value range [0xA-0x0C80] for [100ms - 32s]. + * @return HCIStatusCode::SUCCESS if the command has been accepted, otherwise HCIStatusCode may disclose reason for rejection. + */ + HCIStatusCode connectLE(const uint16_t le_scan_interval=24, const uint16_t le_scan_window=24, + const uint16_t conn_interval_min=12, const uint16_t conn_interval_max=12, + const uint16_t conn_latency=0, const uint16_t supervision_timeout=getHCIConnSupervisorTimeout(0, 15)); + + /** + * Establish a HCI BDADDR_BREDR connection to this device. + * <p> + * BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.5 Create Connection command + * </p> + * <p> + * If this device's addressType is not BDADDR_BREDR, + * HCIStatusCode::UNACCEPTABLE_CONNECTION_PARAM is being returned. + * </p> + * <p> + * The actual new connection handle will be delivered asynchronous and + * the connection event can be caught via AdapterStatusListener::deviceConnected(..), + * or if failed via AdapterStatusListener::deviceDisconnected(..). + * </p> + * <p> + * The device is tracked by the managing adapter. + * </p> + * <p> + * The associated BTAdapter's HCIHandler instance is used to connect, + * see HCIHandler::create_conn(). + * </p> + * @return HCIStatusCode::SUCCESS if the command has been accepted, otherwise HCIStatusCode may disclose reason for rejection. + */ + HCIStatusCode connectBREDR(const uint16_t pkt_type=HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5, + const uint16_t clock_offset=0x0000, const uint8_t role_switch=0x01); + + /** + * Establish a default HCI connection to this device, using certain default parameter. + * <p> + * BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command <br> + * BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.5 Create Connection command + * </p> + * <p> + * Depending on this device's addressType, + * either a BREDR (BDADDR_BREDR) or LE (BDADDR_LE_PUBLIC, BDADDR_LE_RANDOM) connection is attempted.<br> + * If unacceptable, HCIStatusCode::UNACCEPTABLE_CONNECTION_PARAM is being returned. + * </p> + * <p> + * The actual new connection handle will be delivered asynchronous and + * the connection event can be caught via AdapterStatusListener::deviceConnected(..), + * or if failed via AdapterStatusListener::deviceDisconnected(..). + * <p> + * The device is tracked by the managing adapter. + * </p> + * <p> + * See connectLE() and connectBREDR() for more details. + * </p> + * @return HCIStatusCode::SUCCESS if the command has been accepted, otherwise HCIStatusCode may disclose reason for rejection. + */ + HCIStatusCode connectDefault(); + + + /** Return the HCI connection handle to the LE or BREDR peer, zero if not connected. */ + uint16_t getConnectionHandle() const noexcept { return hciConnHandle; } + + /** + * Disconnect the LE or BREDR peer's GATT and HCI connection. + * <p> + * BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.6 Disconnect command + * </p> + * <p> + * The actual disconnect event will be delivered asynchronous and + * the connection event can be caught via AdapterStatusListener::deviceDisconnected(..). + * </p> + * <p> + * The device will be removed from the managing adapter's connected devices + * when AdapterStatusListener::deviceDisconnected(..) has been received. + * </p> + * <p> + * An open GATTHandler will also be closed.<br> + * The connection to this device is removed, removing all connected profiles. + * </p> + * <p> + * An application using one thread per device and rapid connect, should either use disconnect() or remove(), + * but never issue remove() after disconnect(). Doing so would eventually delete the device being already + * in use by another thread due to discovery post disconnect! + * </p> + * <p> + * The associated BTAdapter's HCIHandler instance is used to disconnect, + * see HCIHandler::disconnect(). + * </p> + * @return HCIStatusCode::SUCCESS if the command has been accepted, otherwise HCIStatusCode may disclose reason for rejection. + */ + HCIStatusCode disconnect(const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION ) noexcept; + + /** + * Returns the available ::SMPKeyType mask for the responder (LL slave) or initiator (LL master). + * @param responder if true, queries the responder (LL slave) key, otherwise the initiator (LL master) key. + * @return ::SMPKeyType mask result + */ + SMPKeyType getAvailableSMPKeys(const bool responder) const noexcept; + + /** + * Returns a copy of the Long Term Key (LTK) info, valid after connection and SMP pairing has been completed. + * @param responder true will return the responder's LTK info (remote device, LL slave), otherwise the initiator's (the LL master). + * @return the resulting key. SMPLongTermKeyInfo::enc_size will be zero if invalid. + * @see ::SMPPairingState::COMPLETED + * @see AdapterStatusListener::deviceReady() + */ + SMPLongTermKeyInfo getLongTermKeyInfo(const bool responder) const noexcept; + + /** + * Sets the long term ket (LTK) info of this device to reuse pre-paired encryption. + * <p> + * Must be called before connecting to this device, otherwise HCIStatusCode::CONNECTION_ALREADY_EXISTS will be returned. + * </p> + * @param ltk the pre-paired encryption LTK + * @return ::HCIStatusCode::SUCCESS if successful, otherwise the appropriate error code. + */ + HCIStatusCode setLongTermKeyInfo(const SMPLongTermKeyInfo& ltk) noexcept; + + /** + * Returns a copy of the Signature Resolving Key (LTK) info, valid after connection and SMP pairing has been completed. + * @param responder true will return the responder's LTK info (remote device, LL slave), otherwise the initiator's (the LL master). + * @return the resulting key + * @see ::SMPPairingState::COMPLETED + * @see AdapterStatusListener::deviceReady() + */ + SMPSignatureResolvingKeyInfo getSignatureResolvingKeyInfo(const bool responder) const noexcept; + + /** + * Unpairs this device from the adapter while staying connected. + * <p> + * All keys will be cleared within the adapter and host implementation.<br> + * Should rarely being used by user.<br> + * Internally being used to re-start pairing if GATT connection fails + * in PairingMode::PRE_PAIRED mode. + * </p> + * @return HCIStatusCode::SUCCESS or an appropriate error status. + */ + HCIStatusCode unpair() noexcept; + + /** + * Experimental only. + * <pre> + * adapter.stopDiscovery(): Renders pairDevice(..) to fail: Busy! + * pairDevice(..) behaves quite instable within our connected workflow: Not used! + * </pre> + */ + HCIStatusCode pair(const SMPIOCapability io_cap) noexcept; + + /** + * Set the ::BTSecurityLevel used to connect to this device on the upcoming connection. + * <p> + * Method returns false if ::BTSecurityLevel::UNSET has been given, + * operation fails, this device has already being connected, + * or BTDevice::connectLE() or BTDevice::connectBREDR() has been issued already. + * </p> + * <p> + * To ensure a consistent authentication setup, + * it is advised to set ::SMPIOCapability::NO_INPUT_NO_OUTPUT for sec_level <= ::BTSecurityLevel::ENC_ONLY + * using setConnSecurity() as well as an IO capable ::SMPIOCapability value + * for ::BTSecurityLevel::ENC_AUTH or ::BTSecurityLevel::ENC_AUTH_FIPS.<br> + * You may like to consider using setConnSecurityBest(). + * </p> + * @param sec_level ::BTSecurityLevel to be applied, ::BTSecurityLevel::UNSET will be ignored and method fails. + * @see ::BTSecurityLevel + * @see ::SMPIOCapability + * @see getConnSecurityLevel() + * @see setConnIOCapability() + * @see getConnIOCapability() + * @see setConnSecurity() + * @see setConnSecurityBest() + */ + bool setConnSecurityLevel(const BTSecurityLevel sec_level) noexcept; + + /** + * Return the ::BTSecurityLevel, determined when the connection is established. + * @see ::BTSecurityLevel + * @see ::SMPIOCapability + * @see setConnSecurityLevel() + * @see setConnIOCapability() + * @see getConnIOCapability() + * @see setConnSecurity() + * @see setConnSecurityBest() + */ + BTSecurityLevel getConnSecurityLevel() const noexcept; + + /** + * Sets the given ::SMPIOCapability used to connect to this device on the upcoming connection. + * <p> + * Method returns false if ::SMPIOCapability::UNSET has been given, + * operation fails, this device has already being connected, + * or BTDevice::connectLE() or BTDevice::connectBREDR() has been issued already. + * </p> + * @param[in] io_cap ::SMPIOCapability to be applied, ::SMPIOCapability::UNSET will be ignored and method fails. + * @see ::BTSecurityLevel + * @see ::SMPIOCapability + * @see setConnSecurityLevel() + * @see getConnSecurityLevel() + * @see getConnIOCapability() + * @see setConnSecurity() + * @see setConnSecurityBest() + */ + bool setConnIOCapability(const SMPIOCapability io_cap) noexcept; + + /** + * Return the set ::SMPIOCapability value, determined when the connection is established. + * @see ::BTSecurityLevel + * @see ::SMPIOCapability + * @see setConnSecurityLevel() + * @see getConnSecurityLevel() + * @see setConnIOCapability() + * @see setConnSecurity() + * @see setConnSecurityBest() + */ + SMPIOCapability getConnIOCapability() const noexcept; + + /** + * Sets the given ::BTSecurityLevel and ::SMPIOCapability used to connect to this device on the upcoming connection. + * <p> + * Method returns false if ::BTSecurityLevel::UNSET or ::SMPIOCapability::UNSET has been given, + * operation fails, this device has already being connected, + * or BTDevice::connectLE() or BTDevice::connectBREDR() has been issued already. + * </p> + * <p> + * Method either changes both parameter for the upcoming connection or none at all. + * </p> + * @param[in] sec_level ::BTSecurityLevel to be applied, ::BTSecurityLevel::UNSET will be ignored and method fails. + * @param[in] io_cap ::SMPIOCapability to be applied, ::SMPIOCapability::UNSET will be ignored and method fails. + * @see ::BTSecurityLevel + * @see ::SMPIOCapability + * @see setConnSecurityLevel() + * @see getConnSecurityLevel() + * @see setConnIOCapability() + * @see getConnIOCapability() + * @see setConnSecurityBest() + */ + bool setConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapability io_cap) noexcept; + + /** + * Convenience method to determine the best practice ::BTSecurityLevel and ::SMPIOCapability + * based on the given arguments, used to connect to this device on the upcoming connection. + * <pre> + * if( BTSecurityLevel::UNSET < sec_level && SMPIOCapability::UNSET != io_cap ) { + * return setConnSecurity(sec_level, io_cap); + * } else if( BTSecurityLevel::UNSET < sec_level ) { + * if( BTSecurityLevel::ENC_ONLY >= sec_level ) { + * return setConnSecurity(sec_level, SMPIOCapability::NO_INPUT_NO_OUTPUT); + * } else { + * return setConnSecurityLevel(sec_level); + * } + * } else if( SMPIOCapability::UNSET != io_cap ) { + * return setConnIOCapability(io_cap); + * } else { + * return false; + * } + * </pre> + * <p> + * Method returns false if ::BTSecurityLevel::UNSET and ::SMPIOCapability::UNSET has been given, + * operation fails, this device has already being connected, + * or BTDevice::connectLE() or BTDevice::connectBREDR() has been issued already. + * </p> + * <p> + * Method either changes both parameter for the upcoming connection or none at all. + * </p> + * @param[in] sec_level ::BTSecurityLevel to be applied. + * @param[in] io_cap ::SMPIOCapability to be applied. + * @see ::BTSecurityLevel + * @see ::SMPIOCapability + * @see setConnSecurityLevel() + * @see getConnSecurityLevel() + * @see setConnIOCapability() + * @see getConnIOCapability() + * @see setConnSecurityBest() + */ + bool setConnSecurityBest(const BTSecurityLevel sec_level, const SMPIOCapability io_cap) noexcept; + + /** + * Method sets the given passkey entry, see ::PairingMode::PASSKEY_ENTRY_ini. + * <p> + * Call this method if the device shall be securely paired with ::PairingMode::PASSKEY_ENTRY_ini, + * i.e. when notified via AdapterStatusListener::devicePairingState() in state ::SMPPairingState::PASSKEY_EXPECTED. + * </p> + * + * @param passkey used for ::PairingMode::PASSKEY_ENTRY_ini method. + * Will be encrypted before sending to counter-party. + * + * @return HCIStatusCode::SUCCESS if the command has been accepted, otherwise ::HCIStatusCode may disclose reason for rejection. + * @see PairingMode + * @see SMPPairingState + * @see AdapterStatusListener::devicePairingState() + * @see setPairingPasskey() + * @see setPairingNumericComparison() + * @see getPairingMode() + * @see getPairingState() + */ + HCIStatusCode setPairingPasskey(const uint32_t passkey) noexcept; + + HCIStatusCode setPairingPasskeyNegative() noexcept; + + /** + * Method sets the numeric comparison result, see ::PairingMode::NUMERIC_COMPARE_ini. + * <p> + * Call this method if the device shall be securely paired with ::PairingMode::NUMERIC_COMPARE_ini, + * i.e. when notified via AdapterStatusListener::devicePairingState() in state ::SMPPairingState::NUMERIC_COMPARE_EXPECTED. + * </p> + * + * @param equal used for ::PairingMode::NUMERIC_COMPARE_ini method. + * Will be encrypted before sending to counter-party. + * + * @return HCIStatusCode::SUCCESS if the command has been accepted, otherwise ::HCIStatusCode may disclose reason for rejection. + * @see PairingMode + * @see SMPPairingState + * @see AdapterStatusListener::devicePairingState() + * @see setPairingPasskey() + * @see setPairingNumericComparison() + * @see getPairingMode() + * @see getPairingState() + */ + HCIStatusCode setPairingNumericComparison(const bool equal) noexcept; + + /** + * Returns the current ::PairingMode used by the device. + * <p> + * If the device is not paired, the current mode is ::PairingMode::NONE. + * </p> + * <p> + * If the Pairing Feature Exchange is completed, i.e. ::SMPPairingState::FEATURE_EXCHANGE_COMPLETED, + * as notified by AdapterStatusListener::devicePairingState(), + * the current mode reflects the currently used PairingMode. + * </p> + * <p> + * In case the Pairing Feature Exchange is in progress, the current mode is ::PairingMode::NEGOTIATING. + * </p> + * @return current ::PairingMode. + * @see PairingMode + * @see SMPPairingState + * @see AdapterStatusListener::devicePairingState() + * @see setPairingPasskey() + * @see setPairingNumericComparison() + * @see getPairingMode() + * @see getPairingState() + */ + PairingMode getPairingMode() const noexcept; + + /** + * Returns the current ::SMPPairingState. + * <p> + * If the device is not paired, the current state is ::SMPPairingState::NONE. + * </p> + * @see PairingMode + * @see SMPPairingState + * @see AdapterStatusListener::devicePairingState() + * @see setPairingPasskey() + * @see setPairingNumericComparison() + * @see getPairingMode() + * @see getPairingState() + */ + SMPPairingState getPairingState() const noexcept; + + /** + * Disconnects this device via disconnect(..) if getConnected()==true + * and explicitly removes its shared references from the Adapter: + * connected-devices, discovered-devices and shared-devices. + * <p> + * This method shall be issued to ensure no device reference will + * be leaked in a long lived adapter, + * as only its reference within connected-devices and discovered-devices are removed at disconnect. + * </p> + * <p> + * After calling this method, this instance is destroyed and shall not be used anymore! + * </p> + * <p> + * This method is an atomic operation. + * </p> + * <p> + * An application using one thread per device and rapid connect, should either use disconnect() or remove(), + * but never issue remove() after disconnect() if the device is in use. + * </p> + */ + void remove() noexcept; + + /** Returns the connected GATTHandler or nullptr, see connectGATT(), getGattService() and disconnect(). */ + std::shared_ptr<BTGattHandler> getGattHandler() noexcept; + + /** + * Returns a list of shared GATTService available on this device if successful, + * otherwise returns an empty list if an error occurred. + * <p> + * The HCI connectLE(..) or connectBREDR(..) must be performed first, see {@link #connectDefault()}. + * </p> + * <p> + * If this method has been called for the first time or no services has been detected yet, + * a list of GATTService will be discovered. + * <br> + * In case no GATT connection has been established it will be created via connectGATT(). + * </p> + */ + jau::darray<std::shared_ptr<BTGattService>> getGattServices() noexcept; + + /** + * Returns the matching GATTService for the given uuid. + * <p> + * Implementation calls getGattService(). + * </p> + */ + std::shared_ptr<BTGattService> findGattService(std::shared_ptr<uuid_t> const &uuid); + + /** Returns the shared GenericAccess instance, retrieved by {@link #getGattService()} or nullptr if not available. */ + std::shared_ptr<GattGenericAccessSvc> getGattGenericAccess(); + + /** + * Issues a GATT ping to the device, validating whether it is still reachable. + * <p> + * This method could be periodically utilized to shorten the underlying OS disconnect period + * after turning the device off, which lies within 7-13s. + * </p> + * <p> + * In case the device is no more reachable, the GATTHandler will initiate disconnect due to the occurring IO error. + * A disconnect will finally being issued. + * </p> + * <p> + * GATT services must have been initialized via {@link #getGattService()}, otherwise {@code false} is being returned. + * </p> + * @return {@code true} if successful, otherwise false in case no GATT services exists or is not connected .. etc. + */ + bool pingGATT() noexcept; + + /** + * Add the given BTGattCharListener to the listener list if not already present. + * <p> + * Convenience delegation call to GATTHandler + * </p> + * <p> + * To enable the actual BLE notification and/or indication, one needs to call + * BTGattChar::configNotificationIndication(bool, bool, bool[]) + * or BTGattChar::enableNotificationOrIndication(bool enabledState[2]). + * </p> + * @param listener A BTGattCharListener instance, listening to all BluetoothGattCharacteristic events of this device + * @return true if the given listener is not element of the list and has been newly added, otherwise false. + * @throws IllegalStateException if the GATTHandler is null, i.e. not connected + */ + bool addCharListener(std::shared_ptr<BTGattCharListener> l); + + /** + * Remove the given {@link BTGattCharListener} from the listener list. + * <p> + * If the GATTHandler is null, i.e. not connected, {@code false} is being returned. + * </p> + * @param listener A {@link BTGattCharListener} instance + * @return true if the given listener is an element of the list and has been removed, otherwise false. + */ + bool removeCharListener(std::shared_ptr<BTGattCharListener> l) noexcept; + + /** + * Remove all {@link BTGattCharListener} from the list, which are associated to the given {@link BTGattChar}. + * <p> + * Implementation tests all listener's BTGattCharListener::match(const BTGattChar & characteristic) + * to match with the given associated characteristic. + * </p> + * @param associatedCharacteristic the match criteria to remove any BTGattCharListener from the list + * @return number of removed listener. + */ + int removeAllAssociatedCharListener(std::shared_ptr<BTGattChar> associatedCharacteristic) noexcept; + + /** + * Remove all {@link BTGattCharListener} from the list. + * @return number of removed listener. + */ + int removeAllCharListener() noexcept; + }; + + inline bool operator==(const BTDevice& lhs, const BTDevice& rhs) noexcept + { return lhs.getAddressAndType() == rhs.getAddressAndType(); } + + inline bool operator!=(const BTDevice& lhs, const BTDevice& rhs) noexcept + { return !(lhs == rhs); } + +} // namespace direct_bt + +#endif /* BT_DEVICE_HPP_ */ |