diff options
-rw-r--r-- | api/direct_bt/BTTypes.hpp | 2 | ||||
-rw-r--r-- | api/direct_bt/BasicTypes.hpp | 6 | ||||
-rw-r--r-- | api/direct_bt/DBTManager.hpp | 4 | ||||
-rw-r--r-- | api/direct_bt/DBTTypes.hpp | 118 | ||||
-rw-r--r-- | api/direct_bt/GATTHandler.hpp | 3 | ||||
-rw-r--r-- | api/direct_bt/L2CAPComm.hpp | 5 | ||||
-rw-r--r-- | api/direct_bt/MgmtTypes.hpp | 151 | ||||
-rw-r--r-- | api/direct_bt/OctetTypes.hpp | 25 | ||||
-rw-r--r-- | examples/direct_bt_scanner/dbt_scanner.cpp | 34 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTAdapter.java | 105 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTDevice.java | 181 | ||||
-rw-r--r-- | java/jni/direct_bt/DBTAdapter.cxx | 84 | ||||
-rw-r--r-- | java/jni/direct_bt/DBTDevice.cxx | 281 | ||||
-rw-r--r-- | java/jni/direct_bt/helper_dbt.cxx | 28 | ||||
-rw-r--r-- | java/jni/direct_bt/helper_dbt.hpp | 4 | ||||
-rw-r--r-- | src/direct_bt/BTTypes.cpp | 26 | ||||
-rw-r--r-- | src/direct_bt/DBTAdapter.cpp | 58 | ||||
-rw-r--r-- | src/direct_bt/DBTDevice.cpp | 112 | ||||
-rw-r--r-- | src/direct_bt/DBTManager.cpp | 78 | ||||
-rw-r--r-- | src/direct_bt/GATTHandler.cpp | 2 | ||||
-rw-r--r-- | src/direct_bt/L2CAPComm.cpp | 1 |
21 files changed, 1074 insertions, 234 deletions
diff --git a/api/direct_bt/BTTypes.hpp b/api/direct_bt/BTTypes.hpp index db70fad5..5a8f346c 100644 --- a/api/direct_bt/BTTypes.hpp +++ b/api/direct_bt/BTTypes.hpp @@ -448,7 +448,7 @@ namespace direct_bt { uint16_t getDeviceIDVendor() const { return did_vendor; } uint16_t getDeviceIDProduct() const { return did_product; } uint16_t getDeviceIDVersion() const { return did_version; } - + std::string getDeviceIDModalias() const; std::string getSourceString() const; std::string getAddressString() const { return address.toString(); } std::string eirDataMaskToString() const; diff --git a/api/direct_bt/BasicTypes.hpp b/api/direct_bt/BasicTypes.hpp index 461f7287..0c052ddd 100644 --- a/api/direct_bt/BasicTypes.hpp +++ b/api/direct_bt/BasicTypes.hpp @@ -85,10 +85,10 @@ namespace direct_bt { : RuntimeException("IllegalArgumentException", m, file, line) {} }; - class InvalidStateException : public RuntimeException { + class IllegalStateException : public RuntimeException { public: - InvalidStateException(std::string const m, const char* file, int line) noexcept - : RuntimeException("InvalidStateException", m, file, line) {} + IllegalStateException(std::string const m, const char* file, int line) noexcept + : RuntimeException("IllegalStateException", m, file, line) {} }; class UnsupportedOperationException : public RuntimeException { diff --git a/api/direct_bt/DBTManager.hpp b/api/direct_bt/DBTManager.hpp index ff9f0687..3900eeb6 100644 --- a/api/direct_bt/DBTManager.hpp +++ b/api/direct_bt/DBTManager.hpp @@ -133,7 +133,6 @@ namespace direct_bt { void shutdownAdapter(const uint16_t dev_id); bool mgmtEvClassOfDeviceChangedCB(std::shared_ptr<MgmtEvent> e); - bool mgmtEvLocalNameChangedCB(std::shared_ptr<MgmtEvent> e); bool mgmtEvDeviceDiscoveringCB(std::shared_ptr<MgmtEvent> e); bool mgmtEvDeviceFoundCB(std::shared_ptr<MgmtEvent> e); bool mgmtEvDeviceDisconnectedCB(std::shared_ptr<MgmtEvent> e); @@ -246,6 +245,9 @@ namespace direct_bt { bool disconnect(const int dev_id, const EUI48 &peer_bdaddr, const BDAddressType peer_mac_type, const uint8_t reason=0); + std::shared_ptr<ConnectionInfo> getConnectionInfo(const int dev_id, const EUI48 &address, const BDAddressType address_type); + std::shared_ptr<NameAndShortName> setLocalName(const int dev_id, const std::string & name, const std::string & short_name); + /** MgmtEventCallback handling */ /** diff --git a/api/direct_bt/DBTTypes.hpp b/api/direct_bt/DBTTypes.hpp index acbab5ed..ccdb0798 100644 --- a/api/direct_bt/DBTTypes.hpp +++ b/api/direct_bt/DBTTypes.hpp @@ -40,6 +40,7 @@ #include "BTTypes.hpp" #include "HCIComm.hpp" #include "DBTManager.hpp" +#include "GATTHandler.hpp" #include "JavaUplink.hpp" #define JAVA_MAIN_PACKAGE "org/tinyb" @@ -76,11 +77,23 @@ namespace direct_bt { /** Opens a new HCI session on the given BT dev_id and HCI channel. */ HCISession(DBTAdapter &a, const uint16_t channel, const int timeoutMS=HCI_TO_SEND_REQ_POLL_MS); - /** add the new {@link DBTDevice} to the list of connected devices */ - void connected(std::shared_ptr<DBTDevice> & device); + /** + * Add the new {@link DBTDevice} to the list of connected devices, if not already present. + * <p> + * Returns true if the given device is newly connected and has been added to the list of connected devices, + * otherwise false. + * </p> + */ + bool connected(std::shared_ptr<DBTDevice> & device); - /** remove the {@link DBTDevice} from the list of connected devices */ - void disconnected(std::shared_ptr<DBTDevice> & device); + /** + * Remove the {@link DBTDevice} from the list of connected devices. + * <p> + * Returns true if the given device is an element of the list of connected devices and has been removed, + * otherwise false. + * </p> + */ + bool disconnected(std::shared_ptr<DBTDevice> & device); /** * Issues {@link #disconnectAllDevices()} and closes the underlying HCI session. @@ -196,16 +209,19 @@ namespace direct_bt { private: static const int to_connect_ms = 5000; - DBTAdapter const & adapter; + DBTAdapter & adapter; uint64_t ts_update; std::string name; int8_t rssi = 0; int8_t tx_power = 0; + uint16_t appearance = 0; uint16_t connHandle = 0; std::shared_ptr<ManufactureSpecificData> msd = nullptr; std::vector<std::shared_ptr<uuid_t>> services; + std::shared_ptr<GATTHandler> gattHandler = nullptr; + std::recursive_mutex mtx_gatt; - DBTDevice(DBTAdapter const & adapter, EInfoReport const & r); + DBTDevice(DBTAdapter & adapter, EInfoReport const & r); bool addService(std::shared_ptr<uuid_t> const &uuid); bool addServices(std::vector<std::shared_ptr<uuid_t>> const & services); @@ -250,6 +266,7 @@ namespace direct_bt { bool hasName() const { return name.length()>0; } int8_t getRSSI() const { return rssi; } int8_t getTxPower() const { return tx_power; } + uint16_t getAppearance() const { return appearance; } std::shared_ptr<ManufactureSpecificData> const getManufactureSpecificData() const { return msd; } std::vector<std::shared_ptr<uuid_t>> getServices() const { return services; } @@ -260,6 +277,16 @@ namespace direct_bt { std::string toString() const override; /** + * 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 DBTAdapterStatusListener's deviceUpdated(..) method called for notification. + * </p> + */ + std::shared_ptr<ConnectionInfo> getConnectionInfo(); + + /** * Establish a HCI BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM connection to this device. * <p> * If this device's addressType is not BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM, 0 is being returned. @@ -322,8 +349,36 @@ namespace direct_bt { * <p> * The device will be removed from the managing adapter's HCISession instance. * </p> + * <p> + * An open GATTHandler will also be closed via disconnectGATT() + * </p> */ void disconnect(const uint8_t reason=0); + + /** + * Returns a newly established GATT connection or an already open GATT connection. + * <p> + * The HCI le_connect or HCI connect (defaultConnect) must be performed first, + * to produce orderly behavior and best performance. + * </p> + * <p> + * The returned GATTHandler is managed by this device instance + * and closed @ disconnect() or explicitly @ disconnectGATT(). + * May return nullptr if not connected or failure. + * </p> + */ + std::shared_ptr<GATTHandler> connectGATT(int timeoutMS=GATTHandler::Defaults::L2CAP_READER_THREAD_POLL_TIMEOUT); + + /** Returns already opened GATTHandler, see connectGATT(..) and disconnectGATT(). */ + std::shared_ptr<GATTHandler> getGATTHandler(); + + /** + * Explicit disconnecting an open GATTHandler, which is usually performed via disconnect() + * <p> + * Implementation will also discard the GATTHandler reference. + * </p> + */ + void disconnectGATT(); }; inline bool operator<(const DBTDevice& lhs, const DBTDevice& rhs) @@ -347,13 +402,16 @@ namespace direct_bt { DBTManager& mgmt; std::shared_ptr<AdapterInfo> adapterInfo; + NameAndShortName localName; ScanType currentScanType = ScanType::SCAN_TYPE_NONE; volatile bool keepDiscoveringAlive = false; std::shared_ptr<HCISession> session; std::vector<std::shared_ptr<DBTDevice>> discoveredDevices; // all discovered devices std::shared_ptr<DBTAdapterStatusListener> statusListener = nullptr; + std::vector<std::shared_ptr<DBTAdapterStatusListener>> statusListenerList; std::recursive_mutex mtx_discoveredDevices; + std::recursive_mutex mtx_statusListenerList; bool validateDevInfo(); @@ -361,17 +419,22 @@ namespace direct_bt { void sessionClosing(); friend std::shared_ptr<DBTDevice> DBTDevice::getSharedInstance() const; + friend std::shared_ptr<ConnectionInfo> DBTDevice::getConnectionInfo(); - void addDiscoveredDevice(std::shared_ptr<DBTDevice> const &device); + bool addDiscoveredDevice(std::shared_ptr<DBTDevice> const &device); bool mgmtEvDeviceDiscoveringCB(std::shared_ptr<MgmtEvent> e); bool mgmtEvNewSettingsCB(std::shared_ptr<MgmtEvent> e); + bool mgmtEvLocalNameChangedCB(std::shared_ptr<MgmtEvent> e); bool mgmtEvDeviceFoundCB(std::shared_ptr<MgmtEvent> e); bool mgmtEvDeviceConnectedCB(std::shared_ptr<MgmtEvent> e); bool mgmtEvDeviceDisconnectedCB(std::shared_ptr<MgmtEvent> e); void startDiscoveryBackground(); + void sendDeviceUpdated(std::shared_ptr<DBTDevice> device, uint64_t timestamp, EIRDataType updateMask); + + public: const int dev_id; @@ -406,9 +469,50 @@ namespace direct_bt { EUI48 const & getAddress() const { return adapterInfo->address; } std::string getAddressString() const { return adapterInfo->address.toString(); } + + /** + * Returns the system name. + */ std::string getName() const { return adapterInfo->getName(); } /** + * Returns the short system name. + */ + std::string getShortName() const { return adapterInfo->getShortName(); } + + /** + * Returns the local friendly name and short_name. Contains empty strings if not set. + * <p> + * The value is being updated via SET_LOCAL_NAME management event reply. + * </p> + */ + const NameAndShortName & getLocalName() const { return localName; } + + /** + * Sets the local friendly name. + * <p> + * Returns the immediate SET_LOCAL_NAME reply if successful, otherwise nullptr. + * The corresponding management event will be received separately. + * </p> + */ + std::shared_ptr<NameAndShortName> setLocalName(const std::string &name, const std::string &short_name); + + /** + * Set the power state of the adapter. + */ + void setPowered(bool value); + + /** + * Set the discoverable state of the adapter. + */ + void setDiscoverable(bool value); + + /** + * Set the bondable (aka pairable) state of the adapter. + */ + void setBondable(bool value); + + /** * Returns a reference to the used singleton DBTManager instance. */ DBTManager& getManager() const { return mgmt; } diff --git a/api/direct_bt/GATTHandler.hpp b/api/direct_bt/GATTHandler.hpp index 198846c3..ce694eb3 100644 --- a/api/direct_bt/GATTHandler.hpp +++ b/api/direct_bt/GATTHandler.hpp @@ -38,7 +38,6 @@ #include "UUID.hpp" #include "BTTypes.hpp" -#include "DBTTypes.hpp" #include "L2CAPComm.hpp" #include "ATTPDUTypes.hpp" #include "GATTTypes.hpp" @@ -53,6 +52,8 @@ namespace direct_bt { + class DBTDevice; // forward + typedef std::shared_ptr<GATTCharacterisicsDecl> GATTCharacterisicsDeclRef; class GATTNotificationListener { diff --git a/api/direct_bt/L2CAPComm.hpp b/api/direct_bt/L2CAPComm.hpp index 94eef5aa..1353d4ef 100644 --- a/api/direct_bt/L2CAPComm.hpp +++ b/api/direct_bt/L2CAPComm.hpp @@ -37,10 +37,11 @@ #include "UUID.hpp" #include "BTTypes.hpp" -#include "DBTTypes.hpp" namespace direct_bt { + class DBTDevice; // forward + class L2CAPComm { public: enum State : int { @@ -57,7 +58,7 @@ namespace direct_bt { static int l2cap_close_dev(int dd); State state; - std::shared_ptr<DBTDevice> device = nullptr; + std::shared_ptr<DBTDevice> device; const uint16_t psm; const uint16_t cid; const bool pubaddr; diff --git a/api/direct_bt/MgmtTypes.hpp b/api/direct_bt/MgmtTypes.hpp index 10c31cab..2629c87a 100644 --- a/api/direct_bt/MgmtTypes.hpp +++ b/api/direct_bt/MgmtTypes.hpp @@ -310,6 +310,20 @@ namespace direct_bt { /** * mgmt_addr_info { EUI48, uint8_t type }, + */ + class MgmtGetConnectionInfoCmd : public MgmtCommand + { + public: + MgmtGetConnectionInfoCmd(const uint16_t dev_id, const EUI48 &address, const BDAddressType addressType) + : MgmtCommand(MgmtOpcode::GET_CONN_INFO, dev_id, 6+1) + { + pdu.put_eui48(MGMT_HEADER_SIZE, address); + pdu.put_uint8(MGMT_HEADER_SIZE+6, addressType); + } + }; + + /** + * mgmt_addr_info { EUI48, uint8_t type }, * uint8_t pin_len, * uint8_t pin_code[16] */ @@ -342,6 +356,21 @@ namespace direct_bt { }; /** + * uint8_t name[MGMT_MAX_NAME_LENGTH]; + * uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH]; + */ + class MgmtSetLocalNameCmd : public MgmtCommand + { + public: + MgmtSetLocalNameCmd(const uint16_t dev_id, const std::string & name, const std::string & short_name) + : MgmtCommand(MgmtOpcode::SET_LOCAL_NAME, dev_id, MgmtConstU16::MAX_NAME_LENGTH + MgmtConstU16::MAX_SHORT_NAME_LENGTH) + { + pdu.put_string(MGMT_HEADER_SIZE, name, MgmtConstU16::MAX_NAME_LENGTH, true); + pdu.put_string(MGMT_HEADER_SIZE+MgmtConstU16::MAX_NAME_LENGTH, short_name, MgmtConstU16::MAX_SHORT_NAME_LENGTH, true); + } + }; + + /** * uint16_t opcode, * uint16_t dev-id, * uint16_t param_size @@ -904,11 +933,118 @@ namespace direct_bt { checkOpcode(getOpcode(), LOCAL_NAME_CHANGED); pdu.check_range(0, getRequiredSize()); } + MgmtEvtLocalNameChanged(const uint16_t dev_id, const std::string & name, const std::string & short_name) + : MgmtEvent(LOCAL_NAME_CHANGED, dev_id, MgmtConstU16::MAX_NAME_LENGTH + MgmtConstU16::MAX_SHORT_NAME_LENGTH) + { + pdu.put_string(MGMT_HEADER_SIZE, name, MgmtConstU16::MAX_NAME_LENGTH, true); + pdu.put_string(MGMT_HEADER_SIZE+MgmtConstU16::MAX_NAME_LENGTH, short_name, MgmtConstU16::MAX_SHORT_NAME_LENGTH, true); + } - const std::string getName() const { return std::string( (const char*)pdu.get_ptr(MGMT_HEADER_SIZE) ); } - const std::string getShortName() const { return std::string( (const char*)pdu.get_ptr(MGMT_HEADER_SIZE+MgmtConstU16::MAX_NAME_LENGTH) ); } + const std::string getName() const { return pdu.get_string(MGMT_HEADER_SIZE); } + const std::string getShortName() const { return pdu.get_string(MGMT_HEADER_SIZE + MgmtConstU16::MAX_NAME_LENGTH); } }; + /** + * mgmt_addr_info { EUI48, uint8_t type }, + * int8_t rssi, + * int8_t tx_power, + * int8_t max_tx_power; + */ + class ConnectionInfo + { + private: + EUI48 address; + BDAddressType addressType; + int8_t rssi; + int8_t tx_power; + int8_t max_tx_power; + + public: + static int minimumDataSize() { return 6 + 1 + 1 + 1 + 1; } + + ConnectionInfo(const EUI48 &address, BDAddressType addressType, int8_t rssi, int8_t tx_power, int8_t max_tx_power) + : address(address), addressType(addressType), rssi(rssi), tx_power(tx_power), max_tx_power(max_tx_power) {} + + ConnectionInfo(const MgmtEvtCmdComplete & evt) + { + if( MgmtStatus::SUCCESS != evt.getStatus() ) { + throw IllegalArgumentException("Event state: "+evt.toString(), E_FILE_LINE); + } + const int min_size = minimumDataSize(); + if( evt.getDataSize() < min_size ) { + throw IllegalArgumentException("Data size < "+std::to_string(min_size)+": "+evt.toString(), E_FILE_LINE); + } + address = EUI48( evt.getData() ); + addressType = static_cast<BDAddressType>( direct_bt::get_uint8(evt.getData(), 6) ); + rssi = direct_bt::get_int8(evt.getData(), 7); + tx_power = direct_bt::get_int8(evt.getData(), 8); + max_tx_power = direct_bt::get_int8(evt.getData(), 9); + } + + const EUI48 getAddress() const { return address; } + BDAddressType getAddressType() const { return addressType; } + int8_t getRSSI() const { return rssi; } + int8_t getTxPower() const { return tx_power; } + int8_t getMaxTxPower() const { return max_tx_power; } + + std::string toString() const { + return "address="+getAddress().toString()+", addressType "+getBDAddressTypeString(getAddressType())+ + ", rssi "+std::to_string(rssi)+ + ", tx_power[set "+std::to_string(tx_power)+", max "+std::to_string(tx_power)+"]"; + } + }; + + class DBTManager; // forward + class DBTAdapter; // forward + + class NameAndShortName + { + friend class DBTManager; // top manager + friend class DBTAdapter; // direct manager + + private: + std::string name; + std::string short_name; + + protected: + void setName(const std::string v) { name = v; } + void setShortName(const std::string v) { short_name = v; } + + public: + static int minimumDataSize() { return MgmtConstU16::MAX_NAME_LENGTH + MgmtConstU16::MAX_SHORT_NAME_LENGTH; } + + NameAndShortName() + : name(), short_name() {} + + NameAndShortName(const std::string & name, const std::string & short_name) + : name(name), short_name(short_name) {} + + NameAndShortName(const MgmtEvtLocalNameChanged & evt) + : name(evt.getName()), short_name(evt.getShortName()) {} + + NameAndShortName(const MgmtEvtCmdComplete & evt) + { + if( MgmtStatus::SUCCESS != evt.getStatus() ) { + throw IllegalArgumentException("Event state: "+evt.toString(), E_FILE_LINE); + } + const int min_size = minimumDataSize(); + if( evt.getDataSize() < min_size ) { + throw IllegalArgumentException("Data size < "+std::to_string(min_size)+": "+evt.toString(), E_FILE_LINE); + } + name = std::string( (const char*) ( evt.getData() ) ); + short_name = std::string( (const char*) ( evt.getData() + MgmtConstU16::MAX_NAME_LENGTH ) ); + } + + std::string getName() const { return name; } + std::string getShortName() const { return short_name; } + + std::string toString() const { + return "name '"+getName()+"', shortName '"+getShortName()+"'"; + } + }; + + + class MgmtEvtAdapterInfo : public MgmtEvtCmdComplete { protected: @@ -936,13 +1072,10 @@ namespace direct_bt { uint32_t getDevClass() const { return pdu.get_uint8(getDataOffset()+17) | ( pdu.get_uint8(getDataOffset()+18) << 8 ) | ( pdu.get_uint8(getDataOffset()+19) << 16 ); } - const std::string getName() const { return std::string( (const char*)pdu.get_ptr(getDataOffset()+20) ); } - const std::string getShortName() const { return std::string( (const char*)pdu.get_ptr(getDataOffset()+20+MgmtConstU16::MAX_NAME_LENGTH) ); } + std::string getName() const { return pdu.get_string(getDataOffset()+20); } + std::string getShortName() const { return pdu.get_string(getDataOffset()+20+MgmtConstU16::MAX_NAME_LENGTH); } }; - class DBTManager; // forward - class DBTAdapter; // forward - /** Immutable persistent adapter info */ class AdapterInfo { @@ -991,8 +1124,8 @@ namespace direct_bt { } AdapterSetting getCurrentSetting() const { return current_setting; } uint32_t getDevClass() const { return dev_class; } - const std::string getName() const { return name; } - const std::string getShortName() const { return short_name; } + std::string getName() const { return name; } + std::string getShortName() const { return short_name; } std::string toString() const { return "Adapter[id "+std::to_string(dev_id)+", address "+address.toString()+", version "+std::to_string(version)+ diff --git a/api/direct_bt/OctetTypes.hpp b/api/direct_bt/OctetTypes.hpp index 57f8c07c..865d012b 100644 --- a/api/direct_bt/OctetTypes.hpp +++ b/api/direct_bt/OctetTypes.hpp @@ -31,6 +31,7 @@ #include <memory> #include <cstdint> #include <vector> +#include <algorithm> #include <mutex> #include <atomic> @@ -99,6 +100,18 @@ namespace direct_bt { return EUI48(_data+i); } + /** Assumes a null terminated string */ + std::string get_string(const int i) const { + check_range(i, 1); // minimum size + return std::string( (const char*)(_data+i) ); + } + + /** Assumes a string with defined length, not necessarily null terminated */ + std::string get_string(const int i, const int length) const { + check_range(i, length); + return std::string( (const char*)(_data+i), length ); + } + uuid16_t get_uuid16(const int i) const { return uuid16_t(get_uint16(i)); } @@ -168,10 +181,20 @@ namespace direct_bt { } void put_octets(const int i, const TROOctets & v) { - check_range(i, sizeof(v.getSize())); + check_range(i, v.getSize()); memcpy(data() + i, v.get_ptr(), v.getSize()); } + void put_string(const int i, const std::string & v, const int max_len, const bool includeEOS) { + const int size1 = v.size() + ( includeEOS ? 1 : 0 ); + const int size = std::min(size1, max_len); + check_range(i, size); + memcpy(data() + i, v.c_str(), size); + if( size < size1 && includeEOS ) { + *(data() + i + size - 1) = 0; // ensure EOS + } + } + void put_uuid(const int i, const uuid_t & v) { check_range(i, v.getTypeSize()); direct_bt::put_uuid(data(), i, v, true /* littleEndian */); diff --git a/examples/direct_bt_scanner/dbt_scanner.cpp b/examples/direct_bt_scanner/dbt_scanner.cpp index 04c7205d..ee84e8c8 100644 --- a/examples/direct_bt_scanner/dbt_scanner.cpp +++ b/examples/direct_bt_scanner/dbt_scanner.cpp @@ -239,17 +239,17 @@ int main(int argc, char *argv[]) // const uint64_t t4 = direct_bt::getCurrentMilliseconds(); // let's check further for full GATT - direct_bt::GATTHandler gatt(device, GATTHandler::Defaults::L2CAP_READER_THREAD_POLL_TIMEOUT); - if( gatt.connect() ) { - fprintf(stderr, "GATT usedMTU %d (server) -> %d (used)\n", gatt.getServerMTU(), gatt.getUsedMTU()); + std::shared_ptr<direct_bt::GATTHandler> gatt = device->connectGATT(GATTHandler::Defaults::L2CAP_READER_THREAD_POLL_TIMEOUT); + if( nullptr != gatt ) { + fprintf(stderr, "GATT usedMTU %d (server) -> %d (used)\n", gatt->getServerMTU(), gatt->getUsedMTU()); - gatt.setGATTIndicationListener(std::shared_ptr<GATTIndicationListener>(new MyGATTIndicationListener()), true /* sendConfirmation */); - gatt.setGATTNotificationListener(std::shared_ptr<GATTNotificationListener>(new MyGATTNotificationListener())); + gatt->setGATTIndicationListener(std::shared_ptr<GATTIndicationListener>(new MyGATTIndicationListener()), true /* sendConfirmation */); + gatt->setGATTNotificationListener(std::shared_ptr<GATTNotificationListener>(new MyGATTNotificationListener())); #ifdef SCAN_CHARACTERISTIC_DESCRIPTORS std::vector<std::vector<GATTUUIDHandle>> servicesCharacteristicDescriptors; #endif - std::vector<GATTPrimaryServiceRef> & primServices = gatt.discoverCompletePrimaryServices(); + std::vector<GATTPrimaryServiceRef> & primServices = gatt->discoverCompletePrimaryServices(); const uint64_t t5 = direct_bt::getCurrentMilliseconds(); #ifdef SCAN_CHARACTERISTIC_DESCRIPTORS for(size_t i=0; i<primServices.size(); i++) { @@ -271,30 +271,30 @@ int main(int argc, char *argv[]) " total %" PRIu64 " ms\n\n", td45, td47, (t7 - device->getCreationTimestamp()), td07); } - if( gatt.isOpen() ) { - std::shared_ptr<GenericAccess> ga = gatt.getGenericAccess(primServices); + if( gatt->isOpen() ) { + std::shared_ptr<GenericAccess> ga = gatt->getGenericAccess(primServices); if( nullptr != ga ) { fprintf(stderr, " GenericAccess: %s\n\n", ga->toString().c_str()); } } - if( gatt.isOpen() ) { - std::shared_ptr<DeviceInformation> di = gatt.getDeviceInformation(primServices); + if( gatt->isOpen() ) { + std::shared_ptr<DeviceInformation> di = gatt->getDeviceInformation(primServices); if( nullptr != di ) { fprintf(stderr, " DeviceInformation: %s\n\n", di->toString().c_str()); } } - for(size_t i=0; i<primServices.size() && gatt.isOpen(); i++) { + for(size_t i=0; i<primServices.size() && gatt->isOpen(); i++) { GATTPrimaryService & primService = *primServices.at(i); fprintf(stderr, " [%2.2d] Service %s\n", (int)i, primService.toString().c_str()); fprintf(stderr, " [%2.2d] Service Characteristics\n", (int)i); std::vector<GATTCharacterisicsDeclRef> & serviceCharacteristics = primService.characteristicDeclList; - for(size_t j=0; j<serviceCharacteristics.size() && gatt.isOpen(); j++) { + for(size_t j=0; j<serviceCharacteristics.size() && gatt->isOpen(); j++) { GATTCharacterisicsDecl & serviceChar = *serviceCharacteristics.at(j); fprintf(stderr, " [%2.2d.%2.2d] Decla: %s\n", (int)i, (int)j, serviceChar.toString().c_str()); if( serviceChar.hasProperties(GATTCharacterisicsDecl::PropertyBitVal::Read) ) { POctets value(GATTHandler::ClientMaxMTU, 0); - if( gatt.readCharacteristicValue(serviceChar, value) ) { + if( gatt->readCharacteristicValue(serviceChar, value) ) { fprintf(stderr, " [%2.2d.%2.2d] Value: %s\n", (int)i, (int)j, value.toString().c_str()); } } @@ -302,7 +302,7 @@ int main(int argc, char *argv[]) const bool enableNotification = serviceChar.hasProperties(GATTCharacterisicsDecl::PropertyBitVal::Notify); const bool enableIndication = serviceChar.hasProperties(GATTCharacterisicsDecl::PropertyBitVal::Indicate); if( enableNotification || enableIndication ) { - bool res = gatt.configIndicationNotification(*serviceChar.config, enableNotification, enableIndication); + bool res = gatt->configIndicationNotification(*serviceChar.config, enableNotification, enableIndication); fprintf(stderr, " [%2.2d.%2.2d] Config Notification(%d), Indication(%d): Result %d\n", (int)i, (int)j, enableNotification, enableIndication, res); } @@ -318,11 +318,11 @@ int main(int argc, char *argv[]) } // FIXME sleep 1s for potential callbacks .. sleep(1); - gatt.disconnect(); + device->disconnectGATT(); // redundant: also done at gatt->disconnect() } else { - fprintf(stderr, "GATT connect failed: %s\n", gatt.getStateString().c_str()); + fprintf(stderr, "GATT connect failed: %s\n", gatt->getStateString().c_str()); } - device->disconnect(); // OK if not connected + device->disconnect(); // OK if not connected, also issues device->disconnectGATT() -> gatt->disconnect() } // if( ok && nullptr != device ) } diff --git a/java/direct_bt/tinyb/DBTAdapter.java b/java/direct_bt/tinyb/DBTAdapter.java index 11795f28..71ab8394 100644 --- a/java/direct_bt/tinyb/DBTAdapter.java +++ b/java/direct_bt/tinyb/DBTAdapter.java @@ -58,8 +58,11 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter private final long discoveringNotificationRef = 0; private BluetoothNotification<Boolean> discoverableNotification = null; + private boolean isDiscoverable = false; private BluetoothNotification<Boolean> poweredNotification = null; + private boolean isPowered = false; private BluetoothNotification<Boolean> pairableNotification = null; + private boolean isPairable = false; private boolean isOpen = false; private List<BluetoothDevice> discoveredDevices = new ArrayList<BluetoothDevice>(); @@ -100,20 +103,12 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter @Override public String getName() { return name; } - public String getInterfaceName() { - throw new UnsupportedOperationException(); // FIXME - } - @Override public BluetoothType getBluetoothType() { return class_type(); } static BluetoothType class_type() { return BluetoothType.ADAPTER; } @Override - public final BluetoothAdapter clone() - { throw new UnsupportedOperationException(); } // FIXME - - @Override public BluetoothDevice find(final String name, final String address, final long timeoutMS) { final BluetoothManager manager = DBTManager.getBluetoothManager(); return (BluetoothDevice) manager.find(BluetoothType.DEVICE, name, address, this, timeoutMS); @@ -141,19 +136,40 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter return out.toString(); } - /* property accessors: */ + /* Unsupported */ @Override - public String getAlias() { throw new UnsupportedOperationException(); } // FIXME + public long getBluetoothClass() { throw new UnsupportedOperationException(); } // FIXME @Override - public void setAlias(final String value) { throw new UnsupportedOperationException(); } // FIXME + public final BluetoothAdapter clone() { throw new UnsupportedOperationException(); } // FIXME @Override - public long getBluetoothClass() { throw new UnsupportedOperationException(); } // FIXME + public String getInterfaceName() { throw new UnsupportedOperationException(); } // FIXME @Override - public native boolean getPowered(); + public long getDiscoverableTimeout() { throw new UnsupportedOperationException(); } // FIXME + + @Override + public void setDiscoverableTimout(final long value) { throw new UnsupportedOperationException(); } // FIXME + + @Override + public long getPairableTimeout() { throw new UnsupportedOperationException(); } // FIXME + + @Override + public void setPairableTimeout(final long value) { throw new UnsupportedOperationException(); } // FIXME + + @Override + public String getModalias() { throw new UnsupportedOperationException(); } // FIXME + + @Override + public String[] getUUIDs() { throw new UnsupportedOperationException(); } // FIXME + + + /* Java callbacks */ + + @Override + public boolean getPowered() { return isPowered; } @Override public synchronized void enablePoweredNotifications(final BluetoothNotification<Boolean> callback) { @@ -166,10 +182,7 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter } @Override - public native void setPowered(boolean value); - - @Override - public native boolean getDiscoverable(); + public boolean getDiscoverable() { return isDiscoverable; } @Override public synchronized void enableDiscoverableNotifications(final BluetoothNotification<Boolean> callback) { @@ -182,19 +195,7 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter } @Override - public native void setDiscoverable(boolean value); - - @Override - public native long getDiscoverableTimeout(); - - @Override - public native void setDiscoverableTimout(long value); - - @Override - public native BluetoothDevice connectDevice(String address, String addressType); - - @Override - public native boolean getPairable(); + public boolean getPairable() { return isPairable; } @Override public synchronized void enablePairableNotifications(final BluetoothNotification<Boolean> callback) { @@ -206,20 +207,27 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter pairableNotification = null; } + /* Native callbacks */ + + /* Native functionality / properties */ + @Override - public native void setPairable(boolean value); + public native void setPowered(boolean value); @Override - public native long getPairableTimeout(); + public native String getAlias(); @Override - public native void setPairableTimeout(long value); + public native void setAlias(final String value); @Override - public native String[] getUUIDs(); + public native void setDiscoverable(boolean value); @Override - public native String getModalias(); + public native BluetoothDevice connectDevice(String address, String addressType); + + @Override + public native void setPairable(boolean value); /* internal */ @@ -320,21 +328,30 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter final AdapterSettings changedmask, final long timestamp) { System.err.println("Adapter.StatusListener.settings: "+oldmask+" -> "+newmask+", changed "+changedmask+" on "+a); { - final BluetoothNotification<Boolean> _poweredNotification = poweredNotification; - if( null != _poweredNotification && changedmask.isSet(AdapterSettings.SettingType.POWERED) ) { - _poweredNotification.run(newmask.isSet(AdapterSettings.SettingType.POWERED)); + if( changedmask.isSet(AdapterSettings.SettingType.POWERED) ) { + isPowered = newmask.isSet(AdapterSettings.SettingType.POWERED); + final BluetoothNotification<Boolean> _poweredNotification = poweredNotification; + if( null != _poweredNotification ) { + _poweredNotification.run(isPowered); + } } } { - final BluetoothNotification<Boolean> _discoverableNotification = discoverableNotification; - if( null != _discoverableNotification && changedmask.isSet(AdapterSettings.SettingType.DISCOVERABLE) ) { - _discoverableNotification.run(newmask.isSet(AdapterSettings.SettingType.DISCOVERABLE)); + if( changedmask.isSet(AdapterSettings.SettingType.DISCOVERABLE) ) { + isDiscoverable = newmask.isSet(AdapterSettings.SettingType.DISCOVERABLE); + final BluetoothNotification<Boolean> _discoverableNotification = discoverableNotification; + if( null != _discoverableNotification ) { + _discoverableNotification.run( isDiscoverable ); + } } } { - final BluetoothNotification<Boolean> _pairableNotification = pairableNotification; - if( null != _pairableNotification && changedmask.isSet(AdapterSettings.SettingType.BONDABLE) ) { - _pairableNotification.run(newmask.isSet(AdapterSettings.SettingType.BONDABLE)); + if( changedmask.isSet(AdapterSettings.SettingType.BONDABLE) ) { + isPairable = newmask.isSet(AdapterSettings.SettingType.BONDABLE); + final BluetoothNotification<Boolean> _pairableNotification = pairableNotification; + if( null != _pairableNotification ) { + _pairableNotification.run( isPairable ); + } } } } diff --git a/java/direct_bt/tinyb/DBTDevice.java b/java/direct_bt/tinyb/DBTDevice.java index 7e482137..75f053c4 100644 --- a/java/direct_bt/tinyb/DBTDevice.java +++ b/java/direct_bt/tinyb/DBTDevice.java @@ -153,6 +153,11 @@ public class DBTDevice extends DBTObject implements BluetoothDevice } @Override + public DBTAdapter getAdapter() { + return adapter; + } + + @Override public String getAddress() { return address; } @Override @@ -164,10 +169,6 @@ public class DBTDevice extends DBTObject implements BluetoothDevice static BluetoothType class_type() { return BluetoothType.DEVICE; } @Override - public final BluetoothDevice clone() - { throw new UnsupportedOperationException(); } // FIXME - - @Override public BluetoothGattService find(final String UUID, final long timeoutMS) { final BluetoothManager manager = DBTManager.getBluetoothManager(); return (BluetoothGattService) manager.find(BluetoothType.GATT_SERVICE, @@ -186,8 +187,8 @@ public class DBTDevice extends DBTObject implements BluetoothDevice // std::string msdstr = nullptr != msd ? msd->toString() : "MSD[null]"; final String msdstr = "MSD[null]"; out.append("Device[").append(getAddress()).append(", '").append(getName()) - .append("', age ").append(t0-ts_creation).append(" ms, lup ").append(t0-ts_update).append(" ms, rssi ").append(getRSSI()) - .append(", tx-power ").append(getTxPower()).append(", ").append(msdstr).append("]"); + .append("', age ").append(t0-ts_creation).append(" ms, lup ").append(t0-ts_update).append(" ms, connected ").append(connected) + .append(", rssi ").append(getRSSI()).append(", tx-power ").append(getTxPower()).append(", ").append(msdstr).append("]"); /** if(services.size() > 0 ) { out.append("\n"); @@ -203,6 +204,26 @@ public class DBTDevice extends DBTObject implements BluetoothDevice return out.toString(); } + /* Unsupported */ + + @Override + public int getBluetoothClass() { throw new UnsupportedOperationException(); } // FIXME + + @Override + public final BluetoothDevice clone() { throw new UnsupportedOperationException(); } // FIXME + + @Override + public boolean pair() throws BluetoothException { throw new UnsupportedOperationException(); } // FIXME + + @Override + public boolean cancelPairing() throws BluetoothException { throw new UnsupportedOperationException(); } // FIXME + + @Override + public String getAlias() { return null; } // FIXME + + @Override + public void setAlias(final String value) { throw new UnsupportedOperationException(); } // FIXME + /* internal */ private native void initImpl(); @@ -211,14 +232,19 @@ public class DBTDevice extends DBTObject implements BluetoothDevice @Override public final void enableConnectedNotifications(final BluetoothNotification<Boolean> callback) { - connectedNotifications = callback; + synchronized(userCallbackLock) { + userConnectedNotificationsCB = callback; + } + } + @Override + public final void disableConnectedNotifications() { + synchronized(userCallbackLock) { + userConnectedNotificationsCB = null; + } } - private BluetoothNotification<Boolean> connectedNotifications = null; - private boolean connected = false; @Override public final boolean getConnected() { return connected; } - // private native boolean getConnectedImpl(); @Override public final boolean disconnect() throws BluetoothException { @@ -226,8 +252,13 @@ public class DBTDevice extends DBTObject implements BluetoothDevice if( connected ) { res = disconnectImpl(); if( res ) { - connectedNotifications.run(Boolean.FALSE); + // FIXME: Split up - may offload to other thread + // Currently service resolution performed in connectImpl()! + servicesResolved = false; + userServicesResolvedNotificationsCB.run(Boolean.FALSE); + connected = false; + userConnectedNotificationsCB.run(Boolean.FALSE); } } return res; @@ -240,144 +271,152 @@ public class DBTDevice extends DBTObject implements BluetoothDevice if( !connected ) { res = connectImpl(); if( res ) { - connectedNotifications.run(Boolean.TRUE); connected = true; + userConnectedNotificationsCB.run(Boolean.TRUE); + + // FIXME: Split up - may offload to other thread + // Currently service resolution performed in connectImpl()! + servicesResolved = true; + userServicesResolvedNotificationsCB.run(Boolean.TRUE); } } return res; } private native boolean connectImpl() throws BluetoothException; - /* DBT method calls: */ - - @Override - public native boolean connectProfile(String arg_UUID) throws BluetoothException; + /* DBT Java callbacks */ @Override - public native boolean disconnectProfile(String arg_UUID) throws BluetoothException; + public final void enableRSSINotifications(final BluetoothNotification<Short> callback) { + synchronized(userCallbackLock) { + userRSSINotificationsCB = callback; + } + } @Override - public boolean pair() throws BluetoothException - { throw new UnsupportedOperationException(); } // FIXME + public final void disableRSSINotifications() { + synchronized(userCallbackLock) { + userRSSINotificationsCB = null; + } + } - @Override - public native boolean remove() throws BluetoothException; @Override - public boolean cancelPairing() throws BluetoothException - { throw new UnsupportedOperationException(); } // FIXME + public final void enableManufacturerDataNotifications(final BluetoothNotification<Map<Short, byte[]> > callback) { + synchronized(userCallbackLock) { + userManufDataNotificationsCB = callback; + } + } @Override - public native List<BluetoothGattService> getServices(); - - /* property accessors: */ + public final void disableManufacturerDataNotifications() { + synchronized(userCallbackLock) { + userManufDataNotificationsCB = null; + } + } - @Override - public String getAlias() { return null; } // FIXME @Override - public void setAlias(final String value) - { throw new UnsupportedOperationException(); } // FIXME + public final void enableServicesResolvedNotifications(final BluetoothNotification<Boolean> callback) { + synchronized(userCallbackLock) { + userServicesResolvedNotificationsCB = callback; + } + } @Override - public native int getBluetoothClass(); + public void disableServicesResolvedNotifications() { + synchronized(userCallbackLock) { + userServicesResolvedNotificationsCB = null; + } + } @Override - public native short getAppearance(); + public boolean getServicesResolved () { return servicesResolved; } @Override - public native String getIcon(); + public short getAppearance() { return appearance; } - @Override - public native boolean getPaired(); + /* DBT native callbacks */ @Override - public native void enablePairedNotifications(BluetoothNotification<Boolean> callback); + public native void enableBlockedNotifications(BluetoothNotification<Boolean> callback); @Override - public native void disablePairedNotifications(); + public void disableBlockedNotifications() { } // FIXME @Override - public native boolean getTrusted(); + public native void enableServiceDataNotifications(BluetoothNotification<Map<String, byte[]> > callback); @Override - public native void enableTrustedNotifications(BluetoothNotification<Boolean> callback); + public void disableServiceDataNotifications() { } // FIXME @Override - public native void disableTrustedNotifications(); + public native void enablePairedNotifications(BluetoothNotification<Boolean> callback); @Override - public native void setTrusted(boolean value); + public void disablePairedNotifications() { } // FIXME @Override - public native boolean getBlocked(); + public native void enableTrustedNotifications(BluetoothNotification<Boolean> callback); @Override - public native void enableBlockedNotifications(BluetoothNotification<Boolean> callback); + public void disableTrustedNotifications() { } // FIXME - @Override - public native void disableBlockedNotifications(); - @Override - public native void setBlocked(boolean value); + /* DBT native method calls: */ @Override - public native boolean getLegacyPairing(); + public native boolean connectProfile(String arg_UUID) throws BluetoothException; @Override - public native short getRSSI(); + public native boolean disconnectProfile(String arg_UUID) throws BluetoothException; - @Override - public native void enableRSSINotifications(BluetoothNotification<Short> callback); + @Override + public native boolean remove() throws BluetoothException; @Override - public native void disableRSSINotifications(); + public native List<BluetoothGattService> getServices(); - @Override - public native void disableConnectedNotifications(); + /* property accessors: */ @Override - public native String[] getUUIDs(); + public native String getIcon(); @Override - public native String getModalias(); + public native boolean getPaired(); @Override - public native DBTAdapter getAdapter(); + public native boolean getTrusted(); @Override - public native Map<Short, byte[]> getManufacturerData(); + public native void setTrusted(boolean value); @Override - public native void enableManufacturerDataNotifications(BluetoothNotification<Map<Short, byte[]> > callback); + public native boolean getBlocked(); @Override - public native void disableManufacturerDataNotifications(); - + public native void setBlocked(boolean value); @Override - public native Map<String, byte[]> getServiceData(); + public native boolean getLegacyPairing(); @Override - public native void enableServiceDataNotifications(BluetoothNotification<Map<String, byte[]> > callback); + public native short getRSSI(); @Override - public native void disableServiceDataNotifications(); + public native String[] getUUIDs(); @Override - public native short getTxPower (); + public native String getModalias(); @Override - public native boolean getServicesResolved (); + public native Map<Short, byte[]> getManufacturerData(); @Override - public final void enableServicesResolvedNotifications(final BluetoothNotification<Boolean> callback) { - servicesResolvedNotifications = callback; - } - private BluetoothNotification<Boolean> servicesResolvedNotifications = null; + public native Map<String, byte[]> getServiceData(); @Override - public native void disableServicesResolvedNotifications(); + public native short getTxPower (); @Override protected native void deleteImpl(); diff --git a/java/jni/direct_bt/DBTAdapter.cxx b/java/jni/direct_bt/DBTAdapter.cxx index 6a8c4aa6..825285bb 100644 --- a/java/jni/direct_bt/DBTAdapter.cxx +++ b/java/jni/direct_bt/DBTAdapter.cxx @@ -499,3 +499,87 @@ void Java_direct_1bt_tinyb_DBTAdapter_enableDiscoveringNotifications(JNIEnv *env rethrow_and_raise_java_exception(env); } } + +// +// misc +// + +void Java_direct_1bt_tinyb_DBTAdapter_setPowered(JNIEnv *env, jobject obj, jboolean value) { + try { + DBTAdapter *adapter = getInstance<DBTAdapter>(env, obj); + JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); + adapter->setPowered(JNI_TRUE == value ? true : false); + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} + +jstring Java_direct_1bt_tinyb_DBTAdapter_getAlias(JNIEnv *env, jobject obj) { + try { + DBTAdapter *adapter = getInstance<DBTAdapter>(env, obj); + JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); + return from_string_to_jstring(env, adapter->getLocalName().getName()); + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return nullptr; +} + +void Java_direct_1bt_tinyb_DBTAdapter_setAlias(JNIEnv *env, jobject obj, jstring jnewalias) { + try { + DBTAdapter *adapter = getInstance<DBTAdapter>(env, obj); + JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); + std::string newalias = from_jstring_to_string(env, jnewalias); + adapter->setLocalName(newalias, std::string()); + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} + +void Java_direct_1bt_tinyb_DBTAdapter_setDiscoverable(JNIEnv *env, jobject obj, jboolean value) { + try { + DBTAdapter *adapter = getInstance<DBTAdapter>(env, obj); + JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); + adapter->setDiscoverable(JNI_TRUE == value ? true : false); + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} + +jobject Java_direct_1bt_tinyb_DBTAdapter_connectDevice(JNIEnv *env, jobject obj, jstring jaddress, jstring jaddressType) { + try { + DBTAdapter *adapter = getInstance<DBTAdapter>(env, obj); + JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); + std::string saddress = from_jstring_to_string(env, jaddress); + EUI48 address(saddress); + std::shared_ptr<DBTDevice> device = adapter->findDiscoveredDevice(address); + if( nullptr != device ) { + const BDAddressType addressType = fromJavaAdressTypeToBDAddressType(env, jaddressType); + const BDAddressType addressTypeDevice = device->getAddressType(); + if( addressTypeDevice != addressType ) { + // oops? + WARN_PRINT("DBTAdapter::connectDevice: AddressType mismatch, ignoring request: Requested %s, Device %s %s", + getBDAddressTypeString(addressType).c_str(), getBDAddressTypeString(addressTypeDevice).c_str(), + device->toString().c_str()); + } + std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject(); + JavaGlobalObj::check(jDeviceRef, E_FILE_LINE); + + device->defaultConnect(); + return JavaGlobalObj::GetObject(jDeviceRef); + } + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return nullptr; +} + +void Java_direct_1bt_tinyb_DBTAdapter_setPairable(JNIEnv *env, jobject obj, jboolean value) { + try { + DBTAdapter *adapter = getInstance<DBTAdapter>(env, obj); + JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); + adapter->setBondable(JNI_TRUE == value ? true : false); + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} diff --git a/java/jni/direct_bt/DBTDevice.cxx b/java/jni/direct_bt/DBTDevice.cxx index 68634807..e5da621b 100644 --- a/java/jni/direct_bt/DBTDevice.cxx +++ b/java/jni/direct_bt/DBTDevice.cxx @@ -75,64 +75,305 @@ void Java_direct_1bt_tinyb_DBTDevice_deleteImpl(JNIEnv *env, jobject obj) } } -jshort Java_direct_1bt_tinyb_DBTDevice_getRSSI(JNIEnv *env, jobject obj) +jboolean Java_direct_1bt_tinyb_DBTDevice_disconnectImpl(JNIEnv *env, jobject obj) { try { DBTDevice *device = getInstance<DBTDevice>(env, obj); - return device->getRSSI(); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + device->disconnect(); } catch(...) { rethrow_and_raise_java_exception(env); } - return 0; + return JNI_TRUE; } -jshort Java_direct_1bt_tinyb_DBTDevice_getTxPower(JNIEnv *env, jobject obj) +jboolean Java_direct_1bt_tinyb_DBTDevice_connectImpl(JNIEnv *env, jobject obj) { try { DBTDevice *device = getInstance<DBTDevice>(env, obj); - return device->getTxPower(); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + uint16_t chandle = device->defaultConnect(); + if( 0 == chandle ) { + return JNI_FALSE; + } + std::shared_ptr<GATTHandler> gatt = device->connectGATT(); + if( nullptr != gatt ) { + // FIXME: Split up - may offload to other thread + std::vector<GATTPrimaryServiceRef> & primServices = gatt->discoverCompletePrimaryServices(); + + std::shared_ptr<GenericAccess> ga = gatt->getGenericAccess(primServices); + if( nullptr != ga ) { + env->SetShortField(obj, getField(env, obj, "appearance", "S"), static_cast<jshort>(ga->category)); + java_exception_check_and_throw(env, E_FILE_LINE); + DBG_PRINT("GATT connected to GenericAccess: %s", ga->toString().c_str()); + } +#if 0 + std::shared_ptr<DeviceInformation> di = gatt->getDeviceInformation(primServices); + if( nullptr != di ) { + DBG_PRINT("GATT connected to DeviceInformation: %s", di->toString().c_str()); + } +#endif + return JNI_TRUE; + } } catch(...) { rethrow_and_raise_java_exception(env); } - return 0; + return JNI_FALSE; } -#if 0 -jboolean Java_direct_1bt_tinyb_DBTDevice_getConnectedImpl(JNIEnv *env, jobject obj) +// +// getter +// + +jobject Java_direct_1bt_tinyb_DBTDevice_getServices(JNIEnv *env, jobject obj) { + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return nullptr; + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return nullptr; +} + +jshort Java_direct_1bt_tinyb_DBTDevice_getAppearance(JNIEnv *env, jobject obj) { try { DBTDevice *device = getInstance<DBTDevice>(env, obj); - const DBTAdapter & adapter = device->getAdapter(); - std::shared_ptr<HCISession> session = adapter.getOpenSession(); - if( nullptr == session || !session->isOpen() ) { - return JNI_FALSE; - } - return nullptr != session->findConnectedLEDevice(device->getAddress()); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return (jshort) device->getAppearance(); + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return (jshort)0; +} + +jstring Java_direct_1bt_tinyb_DBTDevice_getIcon(JNIEnv *env, jobject obj) +{ + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return nullptr; // FIXME + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return nullptr; // FIXME; +} + +jboolean Java_direct_1bt_tinyb_DBTDevice_getPaired(JNIEnv *env, jobject obj) +{ + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return JNI_FALSE; // FIXME } catch(...) { rethrow_and_raise_java_exception(env); } return JNI_FALSE; } -#endif -jboolean Java_direct_1bt_tinyb_DBTDevice_disconnectImpl(JNIEnv *env, jobject obj) +jboolean Java_direct_1bt_tinyb_DBTDevice_getTrusted(JNIEnv *env, jobject obj) { try { DBTDevice *device = getInstance<DBTDevice>(env, obj); - device->disconnect(); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return JNI_FALSE; // FIXME } catch(...) { rethrow_and_raise_java_exception(env); } - return JNI_TRUE; + return JNI_FALSE; } -jboolean Java_direct_1bt_tinyb_DBTDevice_connectImpl(JNIEnv *env, jobject obj) +void Java_direct_1bt_tinyb_DBTDevice_setTrusted(JNIEnv *env, jobject obj, jboolean value) { try { DBTDevice *device = getInstance<DBTDevice>(env, obj); - return device->defaultConnect(); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + (void)value; + // FIXME + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} + +jboolean Java_direct_1bt_tinyb_DBTDevice_getBlocked(JNIEnv *env, jobject obj) +{ + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return JNI_FALSE; // FIXME } catch(...) { rethrow_and_raise_java_exception(env); } return JNI_FALSE; } + +void Java_direct_1bt_tinyb_DBTDevice_setBlocked(JNIEnv *env, jobject obj, jboolean value) +{ + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + (void)value; + // FIXME + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} + +jboolean JNICALL Java_direct_1bt_tinyb_DBTDevice_getLegacyPairing(JNIEnv *env, jobject obj) +{ + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return JNI_FALSE; // FIXME + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return JNI_FALSE; +} + +jshort Java_direct_1bt_tinyb_DBTDevice_getRSSI(JNIEnv *env, jobject obj) +{ + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return (jshort) device->getRSSI(); + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return 0; +} + + +jobjectArray Java_direct_1bt_tinyb_DBTDevice_getUUIDs(JNIEnv *env, jobject obj) +{ + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return nullptr; // FIXME + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return nullptr; +} + +jstring Java_direct_1bt_tinyb_DBTDevice_getModalias(JNIEnv *env, jobject obj) +{ + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return nullptr; // FIXME + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return nullptr; +} + +jobject Java_direct_1bt_tinyb_DBTDevice_getManufacturerData(JNIEnv *env, jobject obj) +{ + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return nullptr; // FIXME + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return nullptr; +} + +jobject Java_direct_1bt_tinyb_DBTDevice_getServiceData(JNIEnv *env, jobject obj) +{ + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return nullptr; // FIXME + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return nullptr; +} + +jshort Java_direct_1bt_tinyb_DBTDevice_getTxPower(JNIEnv *env, jobject obj) +{ + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + return (jshort) device->getTxPower(); + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return 0; +} + +#if 0 +// +// Connected +// + +static void disableConnectedNotifications(JNIEnv *env, jobject obj, DBTManager &mgmt) +{ + InvocationFunc<bool, std::shared_ptr<MgmtEvent>> * funcptr = + getObjectRef<InvocationFunc<bool, std::shared_ptr<MgmtEvent>>>(env, obj, "connectedNotificationRef"); + if( nullptr != funcptr ) { + FunctionDef<bool, std::shared_ptr<MgmtEvent>> funcDef( funcptr ); + funcptr = nullptr; + setObjectRef(env, obj, funcptr, "connectedNotificationRef"); // clear java ref + int count; + if( 1 != ( count = mgmt.removeMgmtEventCallback(MgmtEvent::Opcode::DISCOVERING, funcDef) ) ) { + throw direct_bt::InternalError(std::string("removeMgmtEventCallback of ")+funcDef.toString()+" not 1 but "+std::to_string(count), E_FILE_LINE); + } + } +} +void Java_direct_1bt_tinyb_DBTDevice_disableConnectedNotificationsImpl(JNIEnv *env, jobject obj) +{ + // org.tinyb.BluetoothAdapterStatusListener + try { + DBTDevice *device = getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + DBTManager & mgmt = device->getAdapter().getManager(); + + disableConnectedNotifications(env, obj, mgmt); + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} +void Java_direct_1bt_tinyb_DBTDevice_enableConnectedNotificationsImpl(JNIEnv *env, jobject obj, jobject javaCallback) +{ + // org.tinyb.BluetoothAdapterStatusListener + try { + DBTDevice *device= getInstance<DBTDevice>(env, obj); + JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE); + DBTAdapter & adapter = device->getAdapter(); + DBTManager & mgmt = adapter.getManager(); + + disableConnectedNotifications(env, obj, mgmt); + + bool(*nativeCallback)(JNIGlobalRef&, std::shared_ptr<MgmtEvent>) = + [](JNIGlobalRef& javaCallback_ref, std::shared_ptr<MgmtEvent> e)->bool { + const MgmtEvtConnected &event = *static_cast<const MgmtEvtConnected *>(e.get()); + + jclass notification = search_class(*jni_env, *javaCallback_ref); + jmethodID method = search_method(*jni_env, notification, "run", "(Ljava/lang/Object;)V", false); + jni_env->DeleteLocalRef(notification); + + jclass boolean_cls = search_class(*jni_env, "java/lang/Boolean"); + jmethodID constructor = search_method(*jni_env, boolean_cls, "<init>", "(Z)V", false); + + jobject result = jni_env->NewObject(boolean_cls, constructor, event.getEnabled() ? JNI_TRUE : JNI_FALSE); + jni_env->DeleteLocalRef(boolean_cls); + + jni_env->CallVoidMethod(*javaCallback_ref, method, result); + jni_env->DeleteLocalRef(result); + return true; + }; + // move JNIGlobalRef into CaptureInvocationFunc and operator== includes javaCallback comparison + FunctionDef<bool, std::shared_ptr<MgmtEvent>> funcDef = bindCaptureFunc(JNIGlobalRef(javaCallback), nativeCallback); + setObjectRef(env, obj, funcDef.cloneFunction(), "discoveringNotificationRef"); // set java ref + mgmt.addMgmtEventCallback(adapter->dev_id, MgmtEvent::Opcode::DISCOVERING, funcDef); + mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DEVICE_DISCONNECTED, bindMemberFunc(this, &DBTAdapter::mgmtEvDeviceDisconnectedCB)); + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} + +#endif diff --git a/java/jni/direct_bt/helper_dbt.cxx b/java/jni/direct_bt/helper_dbt.cxx index b3431edc..06edc598 100644 --- a/java/jni/direct_bt/helper_dbt.cxx +++ b/java/jni/direct_bt/helper_dbt.cxx @@ -63,3 +63,31 @@ jobject direct_bt::convert_vector_to_jobject(JNIEnv *env, std::vector<std::share #endif +static std::string jStringEmpty(""); +static std::string jAddressTypePublic("public"); +static std::string jAddressTypeRandom("random"); + +BDAddressType direct_bt::fromJavaAdressTypeToBDAddressType(JNIEnv *env, jstring jAddressType) { + if( nullptr != jAddressType ) { + std::string saddressType = from_jstring_to_string(env, jAddressType); + if( jAddressTypePublic == saddressType ) { + return BDAddressType::BDADDR_LE_PUBLIC; + } + if( jAddressTypeRandom == saddressType ) { + return BDAddressType::BDADDR_LE_RANDOM; + } + } + return BDAddressType::BDADDR_BREDR; +} +jstring direct_bt::fromBDAddressTypeToJavaAddressType(JNIEnv *env, BDAddressType bdAddressType) { + switch( bdAddressType ) { + case BDAddressType::BDADDR_LE_PUBLIC: + return from_string_to_jstring(env, jAddressTypePublic); + case BDAddressType::BDADDR_LE_RANDOM: + return from_string_to_jstring(env, jAddressTypeRandom); + case BDAddressType::BDADDR_BREDR: + // fall through intended + default: + return from_string_to_jstring(env, jStringEmpty); + } +} diff --git a/java/jni/direct_bt/helper_dbt.hpp b/java/jni/direct_bt/helper_dbt.hpp index 5598c011..0264c795 100644 --- a/java/jni/direct_bt/helper_dbt.hpp +++ b/java/jni/direct_bt/helper_dbt.hpp @@ -31,6 +31,7 @@ #include "direct_bt/JavaUplink.hpp" #include "direct_bt/BasicTypes.hpp" +#include "direct_bt/BTAddress.hpp" namespace direct_bt { @@ -108,6 +109,9 @@ namespace direct_bt { #endif + BDAddressType fromJavaAdressTypeToBDAddressType(JNIEnv *env, jstring jAddressType); + jstring fromBDAddressTypeToJavaAddressType(JNIEnv *env, BDAddressType bdAddressType); + } // namespace direct_bt #endif /* HELPER_DBT_HPP_ */ diff --git a/src/direct_bt/BTTypes.cpp b/src/direct_bt/BTTypes.cpp index a2b770f0..050da3a3 100644 --- a/src/direct_bt/BTTypes.cpp +++ b/src/direct_bt/BTTypes.cpp @@ -231,6 +231,32 @@ std::string EInfoReport::toString() const { return out; } +std::string EInfoReport::getDeviceIDModalias() const { + char *cstr = NULL; + int length; + + switch (did_source) { + case 0x0001: + length = asprintf(&cstr, "bluetooth:v%04Xp%04Xd%04X", did_vendor, did_product, did_version); + break; + case 0x0002: + length = asprintf(&cstr, "usb:v%04Xp%04Xd%04X", did_vendor, did_product, did_version); + break; + default: + length = asprintf(&cstr, "source<0x%X>:v%04Xp%04Xd%04X", did_source, did_vendor, did_product, did_version); + break; + } + if( 0 >= length ) { + if( NULL != cstr ) { + free(cstr); + } + return std::string(); + } + std::string res(cstr); + free(cstr); + return res; +} + // ************************************************* // ************************************************* // ************************************************* diff --git a/src/direct_bt/DBTAdapter.cpp b/src/direct_bt/DBTAdapter.cpp index 448ca04f..429ac49e 100644 --- a/src/direct_bt/DBTAdapter.cpp +++ b/src/direct_bt/DBTAdapter.cpp @@ -39,6 +39,7 @@ #include "HCIIoctl.hpp" #include "HCIComm.hpp" #include "DBTTypes.hpp" +#include "BasicAlgos.hpp" extern "C" { #include <inttypes.h> @@ -59,25 +60,28 @@ HCISession::HCISession(DBTAdapter &a, const uint16_t channel, const int timeoutM name(name_counter.fetch_add(1)) {} -void HCISession::connected(std::shared_ptr<DBTDevice> & device) { +bool HCISession::connected(std::shared_ptr<DBTDevice> & device) { const std::lock_guard<std::recursive_mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor for (auto it = connectedDevices.begin(); it != connectedDevices.end(); ++it) { if ( *device == **it ) { - return; // already connected + return false; // already connected } } connectedDevices.push_back(device); + return true; } -void HCISession::disconnected(std::shared_ptr<DBTDevice> & device) { +bool HCISession::disconnected(std::shared_ptr<DBTDevice> & device) { const std::lock_guard<std::recursive_mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor for (auto it = connectedDevices.begin(); it != connectedDevices.end(); ) { if ( **it == *device ) { it = connectedDevices.erase(it); + return true; } else { ++it; } } + return false; } int HCISession::disconnectAllDevices(const uint8_t reason) { @@ -161,6 +165,7 @@ bool DBTAdapter::validateDevInfo() { adapterInfo = mgmt.getAdapterInfo(dev_id); mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DISCOVERING, bindMemberFunc(this, &DBTAdapter::mgmtEvDeviceDiscoveringCB)); mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::NEW_SETTINGS, bindMemberFunc(this, &DBTAdapter::mgmtEvNewSettingsCB)); + mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::LOCAL_NAME_CHANGED, bindMemberFunc(this, &DBTAdapter::mgmtEvLocalNameChangedCB)); mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DEVICE_CONNECTED, bindMemberFunc(this, &DBTAdapter::mgmtEvDeviceConnectedCB)); mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DEVICE_DISCONNECTED, bindMemberFunc(this, &DBTAdapter::mgmtEvDeviceDisconnectedCB)); mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DEVICE_FOUND, bindMemberFunc(this, &DBTAdapter::mgmtEvDeviceFoundCB)); @@ -201,8 +206,8 @@ DBTAdapter::~DBTAdapter() { keepDiscoveringAlive = false; { int count; - if( 5 != ( count = mgmt.removeMgmtEventCallback(dev_id) ) ) { - ERR_PRINT("DBTAdapter removeMgmtEventCallback(DISCOVERING) not 5 but %d", count); + if( 6 != ( count = mgmt.removeMgmtEventCallback(dev_id) ) ) { + ERR_PRINT("DBTAdapter removeMgmtEventCallback(DISCOVERING) not 6 but %d", count); } } statusListenerList.clear(); @@ -217,6 +222,22 @@ DBTAdapter::~DBTAdapter() { DBG_PRINT("DBTAdapter::dtor: XXX"); } +std::shared_ptr<NameAndShortName> DBTAdapter::setLocalName(const std::string &name, const std::string &short_name) { + return mgmt.setLocalName(dev_id, name, short_name); +} + +void DBTAdapter::setPowered(bool value) { + mgmt.setMode(dev_id, MgmtOpcode::SET_POWERED, value ? 1 : 0); +} + +void DBTAdapter::setDiscoverable(bool value) { + mgmt.setMode(dev_id, MgmtOpcode::SET_DISCOVERABLE, value ? 1 : 0); +} + +void DBTAdapter::setBondable(bool value) { + mgmt.setMode(dev_id, MgmtOpcode::SET_BONDABLE, value ? 1 : 0); +} + std::shared_ptr<HCISession> DBTAdapter::open() { if( !valid ) { @@ -331,14 +352,16 @@ std::shared_ptr<DBTDevice> DBTAdapter::findDiscoveredDevice (EUI48 const & mac) } } -void DBTAdapter::addDiscoveredDevice(std::shared_ptr<DBTDevice> const &device) { +bool DBTAdapter::addDiscoveredDevice(std::shared_ptr<DBTDevice> const &device) { const std::lock_guard<std::recursive_mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor for (auto it = discoveredDevices.begin(); it != discoveredDevices.end(); ++it) { if ( *device == **it ) { - // already discovered, just replace + // already discovered + return false; } } discoveredDevices.push_back(device); + return true; } int DBTAdapter::removeDiscoveredDevices() { @@ -395,7 +418,26 @@ bool DBTAdapter::mgmtEvNewSettingsCB(std::shared_ptr<MgmtEvent> e) { }); return true; - } +} + +bool DBTAdapter::mgmtEvLocalNameChangedCB(std::shared_ptr<MgmtEvent> e) { + DBG_PRINT("DBTAdapter::EventCB:LocalNameChanged: %s", e->toString().c_str()); + const MgmtEvtLocalNameChanged &event = *static_cast<const MgmtEvtLocalNameChanged *>(e.get()); + std::string old_name = localName.getName(); + std::string old_shortName = localName.getShortName(); + bool nameChanged = old_name != event.getName(); + bool shortNameChanged = old_shortName != event.getShortName(); + if( nameChanged ) { + localName.setName(event.getName()); + } + if( shortNameChanged ) { + localName.setShortName(event.getShortName()); + } + DBG_PRINT("DBTAdapter::EventCB:LocalNameChanged: Local name: %d: '%s' -> '%s'; short_name: %d: '%s' -> '%s'", + nameChanged, old_name.c_str(), localName.getName().c_str(), + shortNameChanged, old_shortName.c_str(), localName.getShortName().c_str()); + (void)nameChanged; + (void)shortNameChanged; return true; } diff --git a/src/direct_bt/DBTDevice.cpp b/src/direct_bt/DBTDevice.cpp index 9d3c9d4f..cab17154 100644 --- a/src/direct_bt/DBTDevice.cpp +++ b/src/direct_bt/DBTDevice.cpp @@ -40,7 +40,7 @@ using namespace direct_bt; -DBTDevice::DBTDevice(DBTAdapter const & a, EInfoReport const & r) +DBTDevice::DBTDevice(DBTAdapter & a, EInfoReport const & r) : adapter(a), ts_creation(r.getTimestamp()), address(r.getAddress()), addressType(r.getAddressType()) { if( !r.isSet(EIRDataType::BDADDR) ) { @@ -102,7 +102,7 @@ std::string DBTDevice::toString() const { std::string msdstr = nullptr != msd ? msd->toString() : "MSD[null]"; std::string out("Device[address["+getAddressString()+", "+getBDAddressTypeString(getAddressType())+"], name['"+getName()+ "'], age "+std::to_string(t0-ts_creation)+" ms, lup "+std::to_string(t0-ts_update)+" ms, rssi "+std::to_string(getRSSI())+ - ", tx-power "+std::to_string(tx_power)+", "+msdstr+", "+javaObjectToString()+"]"); + ", tx-power "+std::to_string(tx_power)+", appearance "+uint16HexString(appearance)+", "+msdstr+", "+javaObjectToString()+"]"); if(services.size() > 0 ) { out.append("\n"); int i=0; @@ -156,6 +156,12 @@ EIRDataType DBTDevice::update(EInfoReport const & data) { setEIRDataTypeSet(res, EIRDataType::TX_POWER); } } + if( data.isSet(EIRDataType::APPEARANCE) ) { + if( appearance != data.getAppearance() ) { + appearance = data.getAppearance(); + setEIRDataTypeSet(res, EIRDataType::APPEARANCE); + } + } if( data.isSet(EIRDataType::MANUF_DATA) ) { if( msd != data.getManufactureSpecificData() ) { msd = data.getManufactureSpecificData(); @@ -168,6 +174,26 @@ EIRDataType DBTDevice::update(EInfoReport const & data) { return res; } +std::shared_ptr<ConnectionInfo> DBTDevice::getConnectionInfo() { + DBTManager & mgmt = adapter.getManager(); + std::shared_ptr<ConnectionInfo> connInfo = mgmt.getConnectionInfo(adapter.dev_id, address, addressType); + if( nullptr != connInfo ) { + EIRDataType updateMask = EIRDataType::NONE; + if( rssi != connInfo->getRSSI() ) { + rssi = connInfo->getRSSI(); + setEIRDataTypeSet(updateMask, EIRDataType::RSSI); + } + if( tx_power != connInfo->getTxPower() ) { + tx_power = connInfo->getTxPower(); + setEIRDataTypeSet(updateMask, EIRDataType::TX_POWER); + } + if( EIRDataType::NONE != updateMask ) { + adapter.sendDeviceUpdated(getSharedInstance(), getCurrentMilliseconds(), updateMask); + } + } + return connInfo; +} + uint16_t DBTDevice::le_connect(HCIAddressType peer_mac_type, HCIAddressType own_mac_type, uint16_t interval, uint16_t window, uint16_t min_interval, uint16_t max_interval, @@ -183,18 +209,18 @@ uint16_t DBTDevice::le_connect(HCIAddressType peer_mac_type, HCIAddressType own_ ERR_PRINT("DBTDevice::connect: Not a BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM address: %s", toString().c_str()); } - { - // Currently doing nothing, but notifying - DBTManager & mngr = adapter.getManager(); - mngr.create_connection(adapter.dev_id, address, addressType); - } - std::shared_ptr<HCISession> session = adapter.getOpenSession(); if( nullptr == session || !session->isOpen() ) { ERR_PRINT("DBTDevice::le_connect: Not opened"); return 0; } + { + // Currently doing nothing, but notifying + DBTManager & mngr = adapter.getManager(); + mngr.create_connection(adapter.dev_id, address, addressType); + } + connHandle = session->hciComm.le_create_conn( address, peer_mac_type, own_mac_type, interval, window, min_interval, max_interval, latency, supervision_timeout, @@ -221,18 +247,18 @@ uint16_t DBTDevice::connect(const uint16_t pkt_type, const uint16_t clock_offset ERR_PRINT("DBTDevice::connect: Not a BDADDR_BREDR address: %s", toString().c_str()); } - { - // Currently doing nothing, but notifying - DBTManager & mngr = adapter.getManager(); - mngr.create_connection(adapter.dev_id, address, addressType); - } - std::shared_ptr<HCISession> session = adapter.getOpenSession(); if( nullptr == session || !session->isOpen() ) { ERR_PRINT("DBTDevice::connect: Not opened"); return 0; } + { + // Currently doing nothing, but notifying + DBTManager & mngr = adapter.getManager(); + mngr.create_connection(adapter.dev_id, address, addressType); + } + connHandle = session->hciComm.create_conn(address, pkt_type, clock_offset, role_switch); if ( 0 == connHandle ) { @@ -247,17 +273,22 @@ uint16_t DBTDevice::connect(const uint16_t pkt_type, const uint16_t clock_offset uint16_t DBTDevice::defaultConnect() { - if( isLEAddressType() ) { - return le_connect(); - } - if( isBREDRAddressType() ) { - return connect(); + switch( addressType ) { + case BDAddressType::BDADDR_LE_PUBLIC: + return le_connect(HCIAddressType::HCIADDR_LE_PUBLIC); + case BDAddressType::BDADDR_LE_RANDOM: + return le_connect(HCIAddressType::HCIADDR_LE_RANDOM); + case BDAddressType::BDADDR_BREDR: + return connect(); + default: + ERR_PRINT("DBTDevice::defaultConnect: Not a valid address type: %s", toString().c_str()); + return 0; } - ERR_PRINT("DBTDevice::defaultConnect: Not a valid address type: %s", toString().c_str()); - return 0; } void DBTDevice::disconnect(const uint8_t reason) { + disconnectGATT(); + if( 0 == connHandle ) { DBG_PRINT("DBTDevice::disconnect: Not connected"); return; @@ -265,7 +296,7 @@ void DBTDevice::disconnect(const uint8_t reason) { std::shared_ptr<HCISession> session = adapter.getOpenSession(); if( nullptr == session || !session->isOpen() ) { - DBG_PRINT("DBTDevice::disconnect: Not opened"); + DBG_PRINT("DBTDevice::disconnect: Session not opened"); return; } @@ -285,3 +316,40 @@ void DBTDevice::disconnect(const uint8_t reason) { session->disconnected(thisDevice); } +std::shared_ptr<GATTHandler> DBTDevice::connectGATT(int timeoutMS) { + if( 0 == connHandle ) { + DBG_PRINT("DBTDevice::connectGATT: Not connected"); + return nullptr; + } + std::shared_ptr<HCISession> session = adapter.getOpenSession(); + if( nullptr == session || !session->isOpen() ) { + DBG_PRINT("DBTDevice::connectGATT: Session not opened"); + return nullptr; + } + const std::lock_guard<std::recursive_mutex> lock(mtx_gatt); // RAII-style acquire and relinquish via destructor + if( nullptr != gattHandler ) { + return gattHandler; + } + std::shared_ptr<GATTHandler> _gattHandler = std::shared_ptr<GATTHandler>(new GATTHandler(this->getSharedInstance(), timeoutMS)); + if( _gattHandler->connect() ) { + gattHandler = _gattHandler; + } else { + DBG_PRINT("DBTDevice::connectGATT: Connection failed"); + _gattHandler = nullptr; + } + return gattHandler; +} + +std::shared_ptr<GATTHandler> DBTDevice::getGATTHandler() { + const std::lock_guard<std::recursive_mutex> lock(mtx_gatt); // RAII-style acquire and relinquish via destructor + return gattHandler; +} + +void DBTDevice::disconnectGATT() { + const std::lock_guard<std::recursive_mutex> lock(mtx_gatt); // RAII-style acquire and relinquish via destructor + if( nullptr != gattHandler ) { + DBG_PRINT("DBTDevice::disconnectGATT: Disconnecting..."); + gattHandler->disconnect(); + gattHandler = nullptr; + } +} diff --git a/src/direct_bt/DBTManager.cpp b/src/direct_bt/DBTManager.cpp index 508494ef..1e5c9d5c 100644 --- a/src/direct_bt/DBTManager.cpp +++ b/src/direct_bt/DBTManager.cpp @@ -350,7 +350,6 @@ next1: if( ok ) { addMgmtEventCallback(-1, MgmtEvent::Opcode::CLASS_OF_DEV_CHANGED, bindMemberFunc(this, &DBTManager::mgmtEvClassOfDeviceChangedCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::LOCAL_NAME_CHANGED, bindMemberFunc(this, &DBTManager::mgmtEvLocalNameChangedCB)); addMgmtEventCallback(-1, MgmtEvent::Opcode::DISCOVERING, bindMemberFunc(this, &DBTManager::mgmtEvDeviceDiscoveringCB)); addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_FOUND, bindMemberFunc(this, &DBTManager::mgmtEvDeviceFoundCB)); addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_DISCONNECTED, bindMemberFunc(this, &DBTManager::mgmtEvDeviceDisconnectedCB)); @@ -484,8 +483,6 @@ ScanType DBTManager::startDiscovery(const int dev_id, const ScanType scanType) { } else { DBG_PRINT("DBTManager::startDiscovery no-success or invalid res: %s", res1.toString().c_str()); } - } else { - DBG_PRINT("DBTManager::startDiscovery res: NULL"); } return type; } @@ -502,7 +499,6 @@ bool DBTManager::stopDiscovery(const int dev_id, const ScanType type) { const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get()); return MgmtStatus::SUCCESS == res1.getStatus(); } else { - DBG_PRINT("DBTManager::stopDiscovery res: NULL"); return false; } } @@ -550,12 +546,61 @@ bool DBTManager::disconnect(const int dev_id, const EUI48 &peer_bdaddr, const BD sendMgmtEvent(std::shared_ptr<MgmtEvent>(e)); return true; } - } else { - DBG_PRINT("DBTManager::disconnect res: NULL"); } return false; } +std::shared_ptr<ConnectionInfo> DBTManager::getConnectionInfo(const int dev_id, const EUI48 &address, const BDAddressType address_type) { + MgmtGetConnectionInfoCmd req(dev_id, address, address_type); + DBG_PRINT("DBTManager::getConnectionInfo: %s", req.toString().c_str()); + std::shared_ptr<MgmtEvent> res = sendWithReply(req); + if( nullptr == res ) { + DBG_PRINT("DBTManager::getConnectionInfo res: NULL"); + return nullptr; + } + DBG_PRINT("DBTManager::getConnectionInfo res: %s", res->toString().c_str()); + if( res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) { + const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get()); + if( MgmtStatus::SUCCESS == res1.getStatus() ) { + const int min_size = ConnectionInfo::minimumDataSize(); + if( res1.getDataSize() < min_size ) { + ERR_PRINT("Data size < %d: %s", min_size, res1.toString().c_str()); + return nullptr; + } + std::shared_ptr<ConnectionInfo> result(new ConnectionInfo(res1)); + return result; + } + } + return nullptr; +} + +std::shared_ptr<NameAndShortName> DBTManager::setLocalName(const int dev_id, const std::string & name, const std::string & short_name) { + MgmtSetLocalNameCmd req (dev_id, name, short_name); + DBG_PRINT("DBTManager::setLocalName: '%s', short '%s': %s", name.c_str(), short_name.c_str(), req.toString().c_str()); + std::shared_ptr<MgmtEvent> res = sendWithReply(req); + if( nullptr == res ) { + DBG_PRINT("DBTManager::setLocalName res: NULL"); + return nullptr; + } + DBG_PRINT("DBTManager::setLocalName res: %s", res->toString().c_str()); + if( res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) { + const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get()); + if( MgmtStatus::SUCCESS == res1.getStatus() ) { + const int min_size = NameAndShortName::minimumDataSize(); + if( res1.getDataSize() < min_size ) { + ERR_PRINT("Data size < %d: %s", min_size, res1.toString().c_str()); + return nullptr; + } + std::shared_ptr<NameAndShortName> result(new NameAndShortName(res1)); + // explicit LocalNameChanged event + MgmtEvtLocalNameChanged * e = new MgmtEvtLocalNameChanged(dev_id, result->getName(), result->getShortName()); + sendMgmtEvent(std::shared_ptr<MgmtEvent>(e)); + return result; + } + } + return nullptr; +} + /*** * * MgmtEventCallback section @@ -621,27 +666,6 @@ bool DBTManager::mgmtEvClassOfDeviceChangedCB(std::shared_ptr<MgmtEvent> e) { DBG_PRINT("DBTManager::EventCB:ClassOfDeviceChanged: %s", e->toString().c_str()); return true; } -bool DBTManager::mgmtEvLocalNameChangedCB(std::shared_ptr<MgmtEvent> e) { - DBG_PRINT("DBTManager::EventCB:LocalNameChanged: %s", e->toString().c_str()); - const MgmtEvtLocalNameChanged &event = *static_cast<const MgmtEvtLocalNameChanged *>(e.get()); - std::shared_ptr<AdapterInfo> adapterInfo = getAdapterInfo(event.getDevID()); - std::string old_name = adapterInfo->getName(); - std::string old_shortName = adapterInfo->getShortName(); - bool nameChanged = old_name != event.getName(); - bool shortNameChanged = old_shortName != event.getShortName(); - if( nameChanged ) { - adapterInfo->setName(event.getName()); - } - if( shortNameChanged ) { - adapterInfo->setShortName(event.getShortName()); - } - DBG_PRINT("DBTManager::EventCB:LocalNameChanged: Name: %d: '%s' -> '%s'; ShortName: %d: '%s' -> '%s'", - nameChanged, old_name.c_str(), adapterInfo->getName().c_str(), - shortNameChanged, old_shortName.c_str(), adapterInfo->getShortName().c_str()); - (void)nameChanged; - (void)shortNameChanged; - return true; -} bool DBTManager::mgmtEvDeviceDiscoveringCB(std::shared_ptr<MgmtEvent> e) { DBG_PRINT("DBTManager::EventCB:DeviceDiscovering: %s", e->toString().c_str()); const MgmtEvtDiscovering &event = *static_cast<const MgmtEvtDiscovering *>(e.get()); diff --git a/src/direct_bt/GATTHandler.cpp b/src/direct_bt/GATTHandler.cpp index 89f33015..3519a925 100644 --- a/src/direct_bt/GATTHandler.cpp +++ b/src/direct_bt/GATTHandler.cpp @@ -48,6 +48,8 @@ extern "C" { #include "GATTHandler.hpp" +#include "DBTTypes.hpp" + using namespace direct_bt; #define STATE_ENUM(X) \ diff --git a/src/direct_bt/L2CAPComm.cpp b/src/direct_bt/L2CAPComm.cpp index 3d04c897..f303f805 100644 --- a/src/direct_bt/L2CAPComm.cpp +++ b/src/direct_bt/L2CAPComm.cpp @@ -38,6 +38,7 @@ #include "HCIComm.hpp" #include "L2CAPComm.hpp" +#include "DBTTypes.hpp" extern "C" { #include <unistd.h> |