summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/direct_bt/BTTypes.hpp2
-rw-r--r--api/direct_bt/BasicTypes.hpp6
-rw-r--r--api/direct_bt/DBTManager.hpp4
-rw-r--r--api/direct_bt/DBTTypes.hpp118
-rw-r--r--api/direct_bt/GATTHandler.hpp3
-rw-r--r--api/direct_bt/L2CAPComm.hpp5
-rw-r--r--api/direct_bt/MgmtTypes.hpp151
-rw-r--r--api/direct_bt/OctetTypes.hpp25
-rw-r--r--examples/direct_bt_scanner/dbt_scanner.cpp34
-rw-r--r--java/direct_bt/tinyb/DBTAdapter.java105
-rw-r--r--java/direct_bt/tinyb/DBTDevice.java181
-rw-r--r--java/jni/direct_bt/DBTAdapter.cxx84
-rw-r--r--java/jni/direct_bt/DBTDevice.cxx281
-rw-r--r--java/jni/direct_bt/helper_dbt.cxx28
-rw-r--r--java/jni/direct_bt/helper_dbt.hpp4
-rw-r--r--src/direct_bt/BTTypes.cpp26
-rw-r--r--src/direct_bt/DBTAdapter.cpp58
-rw-r--r--src/direct_bt/DBTDevice.cpp112
-rw-r--r--src/direct_bt/DBTManager.cpp78
-rw-r--r--src/direct_bt/GATTHandler.cpp2
-rw-r--r--src/direct_bt/L2CAPComm.cpp1
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>