/* * 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_MANAGER_HPP_ #define DBT_MANAGER_HPP_ #include #include #include #include #include #include #include #include "BTTypes.hpp" #include "BTIoctl.hpp" #include "OctetTypes.hpp" #include "HCIComm.hpp" #include "JavaUplink.hpp" #include "MgmtTypes.hpp" #include "LFRingbuffer.hpp" #include "FunctionDef.hpp" namespace direct_bt { typedef FunctionDef> MgmtEventCallback; class MgmtAdapterEventCallback { private: /** Unique adapter index filter or -1 to listen for all adapter. */ int dev_id; /** MgmtEventCallback instance */ MgmtEventCallback callback; public: MgmtAdapterEventCallback(int _dev_id, const MgmtEventCallback & _callback) : dev_id(_dev_id), callback(_callback) {} MgmtAdapterEventCallback(const MgmtAdapterEventCallback &o) = default; MgmtAdapterEventCallback(MgmtAdapterEventCallback &&o) = default; MgmtAdapterEventCallback& operator=(const MgmtAdapterEventCallback &o) = default; MgmtAdapterEventCallback& operator=(MgmtAdapterEventCallback &&o) = default; /** Unique adapter index filter or -1 to listen for all adapter. */ int getDevID() const { return dev_id; } /** MgmtEventCallback reference */ MgmtEventCallback& getCallback() { return callback; } bool operator==(const MgmtAdapterEventCallback& rhs) const { return dev_id == rhs.dev_id && callback == rhs.callback; } bool operator!=(const MgmtAdapterEventCallback& rhs) const { return !(*this == rhs); } std::string toString() const { return "MgmtAdapterEventCallback[dev_id "+std::to_string(dev_id)+", "+callback.toString()+"]"; } }; typedef std::vector MgmtAdapterEventCallbackList; /** * A thread safe singleton handler of the Linux Kernel's BlueZ manager control channel. *

* Implementation utilizes a lock free ringbuffer receiving data within its separate thread. *

*/ class DBTManager : public JavaUplink { private: struct WhitelistElem { int dev_id; EUI48 address; BDAddressType address_type; HCIWhitelistConnectType ctype; }; std::vector> whitelist; public: enum Defaults : int { /* BT Core Spec v5.2: Vol 3, Part F 3.2.8: Maximum length of an attribute value. */ ClientMaxMTU = 512, /** 3s poll timeout for mgmt reader thread */ MGMT_READER_THREAD_POLL_TIMEOUT = 3000, MGMTEVT_RING_CAPACITY = 256 }; static const pid_t pidSelf; private: const BTMode btMode; POctets rbuffer; HCIComm comm; LFRingbuffer, nullptr> mgmtEventRing; std::thread mgmtReaderThread; std::atomic mgmtReaderRunning; std::atomic mgmtReaderShallStop; /** One MgmtAdapterEventCallbackList per event type, allowing multiple callbacks to be invoked for each event */ std::array(MgmtEvent::Opcode::MGMT_EVENT_TYPE_COUNT)> mgmtAdapterEventCallbackLists; std::recursive_mutex mtx_callbackLists; inline void checkMgmtEventCallbackListsIndex(const MgmtEvent::Opcode opc) const { if( static_cast(opc) >= mgmtAdapterEventCallbackLists.size() ) { throw IndexOutOfBoundsException(static_cast(opc), 1, mgmtAdapterEventCallbackLists.size(), E_FILE_LINE); } } std::vector> adapterInfos; void mgmtReaderThreadImpl(); std::shared_ptr receiveNext(); DBTManager(const BTMode btMode); DBTManager(const DBTManager&) = delete; void operator=(const DBTManager&) = delete; std::shared_ptr initAdapter(const uint16_t dev_id, const BTMode btMode); void shutdownAdapter(const uint16_t dev_id); bool mgmtEvClassOfDeviceChangedCB(std::shared_ptr e); bool mgmtEvDeviceDiscoveringCB(std::shared_ptr e); bool mgmtEvDeviceFoundCB(std::shared_ptr e); bool mgmtEvDeviceDisconnectedCB(std::shared_ptr e); bool mgmtEvDeviceConnectedCB(std::shared_ptr e); bool mgmtEvConnectFailedCB(std::shared_ptr e); bool mgmtEvDevicePinCodeRequestCB(std::shared_ptr e); bool mgmtEvDeviceUnpairedCB(std::shared_ptr e); bool mgmtEvNewConnectionParamCB(std::shared_ptr e); bool mgmtEvDeviceWhitelistAddedCB(std::shared_ptr e); bool mgmtEvDeviceWhilelistRemovedCB(std::shared_ptr e); void sendMgmtEvent(std::shared_ptr event); public: /** * Retrieves the singleton instance. *

* First call will open and initialize the bluetooth kernel. *

*/ static DBTManager& get(const BTMode btMode) { /** * Thread safe starting with C++11 6.7: * * If control enters the declaration concurrently while the variable is being initialized, * the concurrent execution shall wait for completion of the initialization. * * (Magic Statics) * * Avoiding non-working double checked locking. */ static DBTManager s(btMode); return s; } ~DBTManager() { close(); } void close(); std::string get_java_class() const override { return java_class(); } static std::string java_class() { return std::string(JAVA_DBT_PACKAGE "DBTManager"); } BTMode getBTMode() { return btMode; } /** Returns true if this mgmt instance is open and hence valid, otherwise false */ bool isOpen() const { return comm.isOpen(); } std::string toString() const override { return "MgmtHandler["+std::to_string(adapterInfos.size())+" adapter, "+javaObjectToString()+"]"; } /** retrieve information gathered at startup */ /** * Returns list of AdapterInfo with index == dev_id. */ const std::vector> getAdapterInfos() const { return adapterInfos; } /** * Returns number of AdapterInfo with index == dev_id. */ int getAdapterCount() const { return adapterInfos.size(); } /** * Returns the AdapterInfo index (== dev_id) with the given address or -1 if not found. */ int findAdapterInfoIdx(const EUI48 &mac) const; /** * Returns the AdapterInfo (index == dev_id) with the given address or nullptr if not found. */ std::shared_ptr findAdapterInfo(const EUI48 &mac) const; /** * Returns the AdapterInfo (index == dev_id) with the given index. *

* Throws IndexOutOfBoundsException if index is > adapter count. *

*/ std::shared_ptr getAdapterInfo(const int idx) const; /** * Returns the default AdapterInfo (0 == index == dev_id) or nullptr if no adapter is available. */ std::shared_ptr getDefaultAdapterInfo() const { return adapterInfos.size() > 0 ? getAdapterInfo(0) : nullptr; } /** * In case response size check or devID and optional opcode validation fails, * function returns NULL. */ std::shared_ptr sendWithReply(MgmtCommand &req); bool setMode(const int dev_id, const MgmtOpcode opc, const uint8_t mode); /** Start discovery on given adapter dev_id with a ScanType matching the used BTMode. Returns set ScanType. */ ScanType startDiscovery(const int dev_id); /** Start discovery on given adapter dev_id with given ScanType. Returns set ScanType. */ ScanType startDiscovery(const int dev_id, const ScanType type); /** Stop discovery on given adapter dev_id. */ bool stopDiscovery(const int dev_id, const ScanType type); /** * Uploads given connection parameter for given device to the kernel. */ bool uploadConnParam(const int dev_id, const EUI48 &address, const BDAddressType address_type, const uint16_t min_interval=0x000F, const uint16_t max_interval=0x000F, const uint16_t latency=0x0000, const uint16_t timeout=0x0C80); /** * Returns true, if the adapter's device is already whitelisted. */ bool isDeviceWhitelisted(const int dev_id, const EUI48 &address); /** * Add the given device to the adapter's autoconnect whitelist. *

* Make sure {@link uploadConnParam(..)} is invoked first, otherwise performance will lack. *

*

* Method will reject duplicate devices, in which case it should be removed first. *

*/ bool addDeviceToWhitelist(const int dev_id, const EUI48 &address, const BDAddressType address_type, const HCIWhitelistConnectType ctype); /** Remove the given device from the adapter's autoconnect whitelist. */ bool removeDeviceFromWhitelist(const int dev_id, const EUI48 &address, const BDAddressType address_type); /** Remove all previously added devices from the autoconnect whitelist. Returns number of removed devices. */ int removeAllDevicesFromWhitelist(); uint16_t create_connection(const int dev_id, const EUI48 &peer_bdaddr, const BDAddressType peer_mac_type, const BDAddressType own_mac_type=BDADDR_LE_PUBLIC, const uint16_t interval=0x0004, const uint16_t window=0x0004, const uint16_t min_interval=0x000F, const uint16_t max_interval=0x000F, const uint16_t latency=0x0000, const uint16_t supervision_timeout=0x0C80, const uint16_t min_ce_length=0x0001, const uint16_t max_ce_length=0x0001, const uint8_t initiator_filter=0); bool disconnect(const int dev_id, const EUI48 &peer_bdaddr, const BDAddressType peer_mac_type, const uint8_t reason=0); std::shared_ptr getConnectionInfo(const int dev_id, const EUI48 &address, const BDAddressType address_type); std::shared_ptr setLocalName(const int dev_id, const std::string & name, const std::string & short_name); /** MgmtEventCallback handling */ /** * Appends the given MgmtEventCallback for the given adapter dev_id to the named MgmtEvent::Opcode list, * if it is not present already (dev_id + opcode + callback). *

* The adapter dev_id allows filtering the events only directed to the given adapter. * Use dev_id -1 to receive the event for all adapter. *

*/ void addMgmtEventCallback(const int dev_id, const MgmtEvent::Opcode opc, const MgmtEventCallback &cb); /** Returns count of removed given MgmtEventCallback from the named MgmtEvent::Opcode list. */ int removeMgmtEventCallback(const MgmtEvent::Opcode opc, const MgmtEventCallback &cb); /** Returns count of removed MgmtEventCallback from the named MgmtEvent::Opcode list matching the given adapter dev_id . */ int removeMgmtEventCallback(const int dev_id); /** Removes all MgmtEventCallbacks from the to the named MgmtEvent::Opcode list. */ void clearMgmtEventCallbacks(const MgmtEvent::Opcode opc); /** Removes all MgmtEventCallbacks from all MgmtEvent::Opcode lists. */ void clearAllMgmtEventCallbacks(); }; } // namespace direct_bt #endif /* DBT_MANAGER_HPP_ */