/* * Author: Sven Gothel * 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 DBT_DEVICE_HPP_ #define DBT_DEVICE_HPP_ #include #include #include #include #include #include #include "DBTTypes.hpp" #include "HCIIoctl.hpp" #include "HCIComm.hpp" #include "MgmtTypes.hpp" #include "SMPHandler.hpp" #include "GATTHandler.hpp" namespace direct_bt { // ************************************************* // ************************************************* // ************************************************* class DBTAdapter; // forward class DBTDevice : public DBTObject { friend DBTAdapter; // managing us: ctor and update(..) during discovery friend GATTHandler; // may issue detailed disconnect(..) private: DBTAdapter & 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 le_features; std::shared_ptr advMSD = nullptr; std::vector> advServices; #if SMP_SUPPORTED_BY_OS std::shared_ptr smpHandler = nullptr; std::recursive_mutex mtx_smpHandler; #endif std::shared_ptr gattHandler = nullptr; std::recursive_mutex mtx_gattHandler; std::recursive_mutex mtx_connect; std::recursive_mutex mtx_data; std::atomic isConnected; std::atomic 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; DBTDevice(DBTAdapter & adapter, EInfoReport const & r); /** Add advertised service (GAP discovery) */ bool addAdvService(std::shared_ptr const &uuid) noexcept; /** Add advertised service (GAP discovery) */ bool addAdvServices(std::vector> const & services) noexcept; /** * Find advertised service (GAP discovery) index * @return index >= 0 if found, otherwise -1 */ int findAdvService(std::shared_ptr 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 sthis, const uint16_t handle, const SMPIOCapability io_cap) noexcept; void notifyLEFeatures(std::shared_ptr sthis, const LEFeatures features) noexcept; /** * Setup L2CAP channel connection to device incl. optional security encryption level off-thread. *

* Will be performed after connectLE(..), i.e. notifyConnected() and notifyLEFeatures(), * initiated by the latter. *

*/ void processL2CAPSetup(std::shared_ptr sthis); /** * Established SMP host connection and security for L2CAP connection if sec_level > BTSecurityLevel::NONE. *

* Will be performed after connectLE(..), i.e. notifyConnected() and notifyLEFeatures().
* Called from processL2CAPSetup, if supported. *

*

* If sec_level > BTSecurityLevel::NONE, sets the BlueZ's L2CAP socket BT_SECURITY sec_level, determining the SMP security mode per connection. *

*

* The SMPHandler is managed by this device instance and closed via disconnectSMP(). *

* * @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 sthis, const BTSecurityLevel sec_level) noexcept; bool checkPairingKeyDistributionComplete(const std::string& timestamp) const noexcept; bool updatePairingState(std::shared_ptr sthis, const MgmtEvent& evt, const HCIStatusCode evtStatus, SMPPairingState claimed_state) noexcept; /** * Forwarded from HCIHandler -> DBTAdapter -> this DBTDevice *

* Will be initiated by processL2CAPSetup()'s security_level setup after connectLE(..), i.e. notifyConnected() and notifyLEFeatures(). *

*/ void hciSMPMsgCallback(std::shared_ptr sthis, std::shared_ptr msg, const HCIACLData::l2cap_frame& source) noexcept; /** * Setup GATT via connectGATT() off-thread. *

*

* Will be performed after connectLE(..), i.e. notifyConnected() and notifyLEFeatures().
* Called from either processL2CAPSetup() w/o security or with SMP security readiness from hciSMPMsgCallback(). *

*/ void processDeviceReady(std::shared_ptr sthis, const uint64_t timestamp); /** * Returns a newly established GATT connection. *

* Will be performed after connectLE(..) via notifyConnected(), processNotifyConnectedOffThread(). *

*

* The GATTHandler is managed by this device instance and closed via disconnectGATT(). *

*/ bool connectGATT(std::shared_ptr 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 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? DBTDevice(const DBTDevice&) = delete; void operator=(const DBTDevice&) = delete; /** * Releases this instance after calling {@link #remove()}. */ ~DBTDevice() 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 */ DBTAdapter & getAdapter() const { return adapter; } /** Returns the shared pointer of this instance managed by the adapter. */ std::shared_ptr 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 const getManufactureSpecificData() const noexcept; /** * Return a list of advertised services as recognized at discovery, pre GATT discovery. *

* To receive a complete list of GATT services including characteristics etc, * use {@link #getGATTServices()}. *

*/ std::vector> 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. *

* Before this method returns, the internal rssi and tx_power will be updated if any changed * and therefore all DBTAdapterStatusListener's deviceUpdated(..) method called for notification. *

*/ std::shared_ptr 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. *

* BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command *

*

* If this device's addressType is not BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM, * HCIStatusCode::UNACCEPTABLE_CONNECTION_PARAM is being returned. *

*

* 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(..). *

*

* The device is tracked by the managing adapter. *

*

* Default parameter values are chosen for using public address resolution * and usual connection latency, interval etc. *

*

* Set window to the same value as the interval, enables continuous scanning. *

*

* The associated DBTAdapter's HCIHandler instance is used to connect, * see HCIHandler::le_create_conn(). *

* * @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. *

* BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.5 Create Connection command *

*

* If this device's addressType is not BDADDR_BREDR, * HCIStatusCode::UNACCEPTABLE_CONNECTION_PARAM is being returned. *

*

* 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(..). *

*

* The device is tracked by the managing adapter. *

*

* The associated DBTAdapter's HCIHandler instance is used to connect, * see HCIHandler::create_conn(). *

* @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. *

* BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.12 LE Create Connection command
* BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.5 Create Connection command *

*

* Depending on this device's addressType, * either a BREDR (BDADDR_BREDR) or LE (BDADDR_LE_PUBLIC, BDADDR_LE_RANDOM) connection is attempted.
* If unacceptable, HCIStatusCode::UNACCEPTABLE_CONNECTION_PARAM is being returned. *

*

* 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(..). *

* The device is tracked by the managing adapter. *

*

* See connectLE() and connectBREDR() for more details. *

* @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. *

* BT Core Spec v5.2: Vol 4, Part E HCI: 7.1.6 Disconnect command *

*

* The actual disconnect event will be delivered asynchronous and * the connection event can be caught via AdapterStatusListener::deviceDisconnected(..). *

*

* The device will be removed from the managing adapter's connected devices * when AdapterStatusListener::deviceDisconnected(..) has been received. *

*

* An open GATTHandler will also be closed.
* The connection to this device is removed, removing all connected profiles. *

*

* 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! *

*

* The associated DBTAdapter's HCIHandler instance is used to disconnect, * see HCIHandler::disconnect(). *

* @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. *

* Must be called before connecting to this device, otherwise HCIStatusCode::CONNECTION_ALREADY_EXISTS will be returned. *

* @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. *

* All keys will be cleared within the adapter and host implementation.
* Should rarely being used by user.
* Internally being used to re-start pairing if GATT connection fails * in PairingMode::PRE_PAIRED mode. *

* @return HCIStatusCode::SUCCESS or an appropriate error status. */ HCIStatusCode unpair() noexcept; /** * Experimental only. *
             *   adapter.stopDiscovery(): Renders pairDevice(..) to fail: Busy!
             *   pairDevice(..) behaves quite instable within our connected workflow: Not used!
             * 
*/ HCIStatusCode pair(const SMPIOCapability io_cap) noexcept; /** * Set the ::BTSecurityLevel used to connect to this device on the upcoming connection. *

* Method returns false if ::BTSecurityLevel::UNSET has been given, * operation fails, this device has already being connected, * or DBTDevice::connectLE() or DBTDevice::connectBREDR() has been issued already. *

*

* 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.
* You may like to consider using setConnSecurityBest(). *

* @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. *

* Method returns false if ::SMPIOCapability::UNSET has been given, * operation fails, this device has already being connected, * or DBTDevice::connectLE() or DBTDevice::connectBREDR() has been issued already. *

* @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. *

* Method returns false if ::BTSecurityLevel::UNSET or ::SMPIOCapability::UNSET has been given, * operation fails, this device has already being connected, * or DBTDevice::connectLE() or DBTDevice::connectBREDR() has been issued already. *

*

* Method either changes both parameter for the upcoming connection or none at all. *

* @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. *
             *   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;
             *   }
             * 
*

* Method returns false if ::BTSecurityLevel::UNSET and ::SMPIOCapability::UNSET has been given, * operation fails, this device has already being connected, * or DBTDevice::connectLE() or DBTDevice::connectBREDR() has been issued already. *

*

* Method either changes both parameter for the upcoming connection or none at all. *

* @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. *

* 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. *

* * @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. *

* 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. *

* * @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. *

* If the device is not paired, the current mode is ::PairingMode::NONE. *

*

* 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. *

*

* In case the Pairing Feature Exchange is in progress, the current mode is ::PairingMode::NEGOTIATING. *

* @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. *

* If the device is not paired, the current state is ::SMPPairingState::NONE. *

* @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. *

* 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. *

*

* After calling this method, this instance is destroyed and shall not be used anymore! *

*

* This method is automatically called @ destructor. *

*

* This method is an atomic operation. *

*

* 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. *

*/ void remove() noexcept; /** Returns the connected GATTHandler or nullptr, see connectGATT(), getGATTServices() and disconnect(). */ std::shared_ptr getGATTHandler() noexcept; /** * Returns a list of shared GATTService available on this device if successful, * otherwise returns an empty list if an error occurred. *

* The HCI connectLE(..) or connectBREDR(..) must be performed first, see {@link #connectDefault()}. *

*

* If this method has been called for the first time or no services has been detected yet, * a list of GATTService will be discovered. *
* In case no GATT connection has been established it will be created via connectGATT(). *

*/ std::vector> getGATTServices() noexcept; /** * Returns the matching GATTService for the given uuid. *

* Implementation calls getGATTServices(). *

*/ std::shared_ptr findGATTService(std::shared_ptr const &uuid); /** Returns the shared GenericAccess instance, retrieved by {@link #getGATTServices()} or nullptr if not available. */ std::shared_ptr getGATTGenericAccess(); /** * Issues a GATT ping to the device, validating whether it is still reachable. *

* This method could be periodically utilized to shorten the underlying OS disconnect period * after turning the device off, which lies within 7-13s. *

*

* 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. *

*

* GATT services must have been initialized via {@link #getGATTServices()}, otherwise {@code false} is being returned. *

* @return {@code true} if successful, otherwise false in case no GATT services exists or is not connected .. etc. */ bool pingGATT() noexcept; /** * Add the given GATTCharacteristicListener to the listener list if not already present. *

* Convenience delegation call to GATTHandler *

*

* To enable the actual BLE notification and/or indication, one needs to call * GATTCharacteristic::configNotificationIndication(bool, bool, bool[]) * or GATTCharacteristic::enableNotificationOrIndication(bool enabledState[2]). *

* @param listener A GATTCharacteristicListener 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 addCharacteristicListener(std::shared_ptr l); /** * Remove the given {@link GATTCharacteristicListener} from the listener list. *

* If the GATTHandler is null, i.e. not connected, {@code false} is being returned. *

* @param listener A {@link GATTCharacteristicListener} instance * @return true if the given listener is an element of the list and has been removed, otherwise false. */ bool removeCharacteristicListener(std::shared_ptr l) noexcept; /** * Remove all {@link GATTCharacteristicListener} from the list, which are associated to the given {@link GATTCharacteristic}. *

* Implementation tests all listener's GATTCharacteristicListener::match(const GATTCharacteristic & characteristic) * to match with the given associated characteristic. *

* @param associatedCharacteristic the match criteria to remove any GATTCharacteristicListener from the list * @return number of removed listener. */ int removeAllAssociatedCharacteristicListener(std::shared_ptr associatedCharacteristic) noexcept; /** * Remove all {@link GATTCharacteristicListener} from the list. * @return number of removed listener. */ int removeAllCharacteristicListener() noexcept; }; inline bool operator==(const DBTDevice& lhs, const DBTDevice& rhs) noexcept { return lhs.getAddressAndType() == rhs.getAddressAndType(); } inline bool operator!=(const DBTDevice& lhs, const DBTDevice& rhs) noexcept { return !(lhs == rhs); } } // namespace direct_bt #endif /* DBT_DEVICE_HPP_ */