herald  2.0.0
concrete_ble_receiver.h
1 // Copyright 2020-2021 Herald Project Contributors
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 
5 #ifndef HERALD_BLE_CONCRETE_RECEIVER_H
6 #define HERALD_BLE_CONCRETE_RECEIVER_H
7 
8 #include "../ble_database.h"
9 #include "../ble_receiver.h"
10 #include "../ble_sensor.h"
11 #include "../ble_transmitter.h"
12 #include "../ble_protocols.h"
13 #include "../bluetooth_state_manager.h"
14 #include "../ble_device_delegate.h"
15 #include "../filter/ble_advert_parser.h"
16 #include "../../payload/payload_data_supplier.h"
17 #include "../../context.h"
18 #include "../../data/sensor_logger.h"
19 #include "../ble_sensor_configuration.h"
20 #include "../ble_coordinator.h"
21 #include "../../datatype/bluetooth_state.h"
22 #include "../ble_mac_address.h"
23 #include "../../zephyr_context.h"
24 
25 // nRF Connect SDK includes
26 #include <bluetooth/bluetooth.h>
27 #include <bluetooth/conn.h>
28 #include <bluetooth/uuid.h>
29 #include <bluetooth/gatt.h>
30 #include <bluetooth/gatt_dm.h>
31 #include <bluetooth/addr.h>
32 #include <bluetooth/scan.h>
33 #include <kernel.h>
34 #include <zephyr/types.h>
35 #include <zephyr.h>
36 
37 // C++17 includes
38 #include <memory>
39 #include <vector>
40 #include <algorithm>
41 #include <optional>
42 #include <map>
43 #include <functional>
44 
45 namespace herald {
46 namespace ble {
47 
48 using namespace herald::datatype;
49 using namespace herald::ble::filter;
50 using namespace herald::payload;
51 
52 
53 // ZEPHYR UTILITY FUNCTIONS
55 uint32_t waitWithTimeout(uint32_t timeoutMillis, k_timeout_t period, std::function<bool()> keepWaiting);
56 
59  : target(id), state(BLEDeviceState::disconnected), connection(NULL), address(),
60  readPayload(), immediateSend(), remoteInstigated(false), inDiscovery(false), isReading(false)
61  {}
62  ConnectedDeviceState(const ConnectedDeviceState& from) = delete;
64  ~ConnectedDeviceState() = default;
65 
66  TargetIdentifier target;
67  BLEDeviceState state;
68  bt_conn* connection;
69  bt_addr_le_t address;
70  PayloadData readPayload;
71  ImmediateSendData immediateSend;
72  bool remoteInstigated;
73  bool inDiscovery;
74  bool isReading;
75 };
76 
77 namespace zephyrinternal {
78 
79  struct bt_uuid_128* getHeraldUUID();
80  struct bt_uuid_128* getHeraldSignalAndroidCharUUID();
81  struct bt_uuid_128* getHeraldSignalIOSCharUUID();
82  struct bt_uuid_128* getHeraldPayloadCharUUID();
83 
84  void setReceiverInstance(herald::zephyrinternal::Callbacks& cbref);
85  void resetReceiverInstance();
86 
87  struct bt_conn_le_create_param* getDefaultCreateParam();
88  struct bt_le_conn_param* getDefaultConnParam();
89 
90  struct bt_le_scan_param* getDefaultScanParam();
91  struct bt_scan_init_param* getScanInitParam();
92 
93  struct bt_gatt_read_params* getReadParams();
94 
95  // static struct bt_conn* conn = NULL;
96 
97  [[maybe_unused]]
98  // NOTE: The below is called multiple times for ONE char value. Keep appending to result until NULL==data.
99  uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err,
100  struct bt_gatt_read_params *params,
101  const void *data, uint16_t length);
102 
103 
104  // void scan_init(void)
105  // {
106  // // int err;
107 
108  // // struct bt_scan_init_param scan_init = {
109  // // .connect_if_match = 0, // no auto connect (handled by herald protocol coordinator)
110  // // .scan_param = NULL,
111  // // .conn_param = BT_LE_CONN_PARAM_DEFAULT
112  // // };
113 
114  // // bt_scan_init(&scan_init);
115  // // bt_scan_cb_register(&scan_cb);
116 
117  // /*
118  // err = bt_scan_filter_add(BT_SCAN_FILTER_TYPE_UUID, herald_uuid);
119  // if (err) {
120  // printk("Scanning filters cannot be set (err %d)\n", err);
121 
122  // return;
123  // }
124 
125  // err = bt_scan_filter_enable(BT_SCAN_UUID_FILTER, false);
126  // if (err) {
127  // printk("Filters cannot be turned on (err %d)\n", err);
128  // }
129  // */
130  // }
131 
132 
133  [[maybe_unused]]
134  void connected(struct bt_conn *conn, uint8_t err);
135 
136  [[maybe_unused]]
137  void disconnected(struct bt_conn *conn, uint8_t reason);
138 
139  [[maybe_unused]]
140  void le_param_updated(struct bt_conn *conn, uint16_t interval,
141  uint16_t latency, uint16_t timeout);
142 
143  struct bt_conn_cb* getConnectionCallbacks();
144 
145  // static bt_addr_le_t *last_addr = BT_ADDR_LE_NONE;
146 
147  [[maybe_unused]]
148  void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type,
149  struct net_buf_simple *buf);
150 
151 
152  // BT_SCAN_CB_INIT(scan_cbs, scan_filter_match, );
153 
154  // void scan_filter_match(struct bt_scan_device_info *device_info,
155  // struct bt_scan_filter_match *filter_match,
156  // bool connectable)
157  // {
158  // char addr[BT_ADDR_LE_STR_LEN];
159 
160  // bt_addr_le_to_str(device_info->recv_info->addr, addr, sizeof(addr));
161 
162  // printk("Filters matched. Address: %s connectable: %s\n",
163  // addr, connectable ? "yes" : "no");
164  // }
165 
166  // void scan_connecting_error(struct bt_scan_device_info *device_info)
167  // {
168  // printk("Connecting failed\n");
169  // }
170 
171  // void scan_connecting(struct bt_scan_device_info *device_info,
172  // struct bt_conn *conn)
173  // {
174  // //default_conn = bt_conn_ref(conn);
175  // }
176 
177  // void scan_filter_no_match(struct bt_scan_device_info *device_info,
178  // bool connectable)
179  // {
180  // int err;
181  // struct bt_conn *conn;
182  // char addr[BT_ADDR_LE_STR_LEN];
183 
184  // if (device_info->recv_info->adv_type == BT_GAP_ADV_TYPE_ADV_DIRECT_IND) {
185  // bt_addr_le_to_str(device_info->recv_info->addr, addr,
186  // sizeof(addr));
187  // printk("Direct advertising received from %s\n", addr);
188  // bt_scan_stop();
189 
190  // err = bt_conn_le_create(device_info->recv_info->addr,
191  // BT_CONN_LE_CREATE_CONN,
192  // device_info->conn_param, &conn);
193 
194  // if (!err) {
195  // default_conn = bt_conn_ref(conn);
196  // bt_conn_unref(conn);
197  // }
198  // }
199  // }
200 
201  // BT_SCAN_CB_INIT(scan_cb, scan_filter_match, scan_filter_no_match,
202  // scan_connecting_error, scan_connecting);
203 
204 
205 
206  // GATT DISCOVERY INTERNAL METHODS
207 
208  void discovery_completed_cb(struct bt_gatt_dm *dm,
209  void *context);
210 
211  void discovery_service_not_found_cb(struct bt_conn *conn,
212  void *context);
213 
214  void discovery_error_found_cb(struct bt_conn *conn,
215  int err,
216  void *context);
217 
218  const struct bt_gatt_dm_cb* getDiscoveryCallbacks();
219 }
220 
221 template <typename ContextT, typename PayloadDataSupplierT, typename BLEDatabaseT, typename SensorDelegateSetT>
222 class ConcreteBLEReceiver : public HeraldProtocolV1Provider, public herald::zephyrinternal::Callbacks /*, public std::enable_shared_from_this<ConcreteBLEReceiver<ContextT>>*/ {
223 public:
224  ConcreteBLEReceiver(ContextT& ctx, BluetoothStateManager& bluetoothStateManager,
225  PayloadDataSupplierT& payloadDataSupplier, BLEDatabaseT& bleDatabase, SensorDelegateSetT& dels)
226  : m_context(ctx), // Herald API guarantees this to be safe
227  m_stateManager(bluetoothStateManager),
228  m_pds(payloadDataSupplier),
229  db(bleDatabase),
230  delegates(dels),
231  connectionStates(),
232  isScanning(false)
233  HLOGGERINIT(ctx,"Sensor","BLE.ConcreteBLEReceiver")
234  {
235  ;
236  }
237 
238  ConcreteBLEReceiver(const ConcreteBLEReceiver& from) = delete;
239  ConcreteBLEReceiver(ConcreteBLEReceiver&& from) = delete;
240 
242  ;
243  }
244 
245  // Coordination overrides - Since v1.2-beta3
246  std::optional<std::reference_wrapper<CoordinationProvider>> coordinationProvider()
247  {
248  return {}; // we don't provide this, ConcreteBLESensor provides this. We provide HeraldV1ProtocolProvider
249  }
250 
251  // bool immediateSend(Data data, const TargetIdentifier& targetIdentifier) override
252  // {
253  // return false;
254  // }
255 
256  // bool immediateSendAll(Data data) override {
257  // return false;
258  // }
259 
260  // Sensor overrides
261  void start()
262  {
263  HTDBG("ConcreteBLEReceiver::start");
264  if (!m_context.getSensorConfiguration().scanningEnabled) {
265  HTDBG("Sensor Configuration has scanning disabled. Returning.");
266  return;
267  }
268  herald::ble::zephyrinternal::setReceiverInstance(*this);
269 
270 
271  // Ensure our zephyr context has bluetooth ready
272  HTDBG("calling start bluetooth");
273  int startOk = m_context.getPlatform().startBluetooth();
274  HTDBG("start bluetooth done");
275  if (0 != startOk) {
276  HTDBG("ERROR starting context bluetooth:-");
277  HTDBG(std::to_string(startOk));
278  }
279 
280  HTDBG("Calling conn cb register");
281  bt_conn_cb_register(zephyrinternal::getConnectionCallbacks());
282  HTDBG("conn cb register done");
283 
284  HTDBG("calling bt scan start");
285  startScanning();
286 
287  HTDBG("ConcreteBLEReceiver::start completed successfully");
288  }
289 
290  void stop()
291  {
292  HTDBG("ConcreteBLEReceiver::stop");
293  if (!m_context.getSensorConfiguration().scanningEnabled) {
294  HTDBG("Sensor Configuration has scanning disabled. Returning.");
295  return;
296  }
297 
298  herald::ble::zephyrinternal::resetReceiverInstance(); // may not destroy the underlying value
299 
300  stopScanning();
301 
302  // Don't stop Bluetooth altogether - this is done by the ZephyrContext->stopBluetooth() function only
303 
304  HTDBG("ConcreteBLEReceiver::stop completed successfully");
305  }
306 
307  // Herald V1 protocol provider overrides
308  // C++17 CALLBACK VERSION:-
309  // void openConnection(const TargetIdentifier& toTarget, const HeraldConnectionCallback& connCallback) override;
310  // void closeConnection(const TargetIdentifier& toTarget, const HeraldConnectionCallback& connCallback) override;
311  // void serviceDiscovery(Activity, CompletionCallback) override;
312  // void readPayload(Activity, CompletionCallback) override;
313  // void immediateSend(Activity, CompletionCallback) override;
314  // void immediateSendAll(Activity, CompletionCallback) override;
315 
316  // NON C++17 VERSION:-
317  bool openConnection(const TargetIdentifier& toTarget) override
318  {
319  HTDBG("openConnection");
320 
321  // Create addr from TargetIdentifier data
322  ConnectedDeviceState& state = findOrCreateState(toTarget);
323  uint8_t val[6] = {0,0,0,0,0,0};
324  Data addrData = toTarget.underlyingData(); // TODO change this to mac for target ID
325  uint8_t t;
326  bool cok = addrData.uint8(0,t);
327  if (cok)
328  val[0] = t;
329  cok = addrData.uint8(1,t);
330  if (cok)
331  val[1] = t;
332  cok = addrData.uint8(2,t);
333  if (cok)
334  val[2] = t;
335  cok = addrData.uint8(3,t);
336  if (cok)
337  val[3] = t;
338  cok = addrData.uint8(4,t);
339  if (cok)
340  val[4] = t;
341  cok = addrData.uint8(5,t);
342  if (cok)
343  val[5] = t;
344  // TODO create a convenience function in Data for the above
345 
346  // TODO don't assume RANDOM (1) in the below
347  bt_addr_le_t tempAddress{1, {{val[0],val[1],val[2],val[3],val[4],val[5]}}};
348  // state.address = BT_ADDR_LE_NONE;
349  bt_addr_le_copy(&state.address, &tempAddress);
350  HTDBG("Address copied. Constituted as:-");
351  // idiot check of copied data
352  Data newAddr(state.address.a.val,6);
353  BLEMacAddress newMac(newAddr);
354  HTDBG((std::string)newMac);
355 
356 
357 
358 
359  // // print out device info
360  // BLEMacAddress mac(addrData);
361  // std::string di("Opening Connection :: Device info: mac=");
362  // di += (std::string)mac;
363  // di += ", os=";
364  // auto devPtr = db.device(toTarget);
365  // auto os = devPtr->operatingSystem();
366  // if (os.has_value()) {
367  // if (herald::ble::BLEDeviceOperatingSystem::ios == os) {
368  // di += "ios";
369  // } else if (herald::ble::BLEDeviceOperatingSystem::android == os) {
370  // di += "android";
371  // }
372  // } else {
373  // di += "unknown";
374  // }
375  // di += ", ignore=";
376  // auto ignore = devPtr->ignore();
377  // if (ignore) {
378  // di += "true";
379  // } else {
380  // di += "false";
381  // }
382 
383  // HTDBG(di);
384 
385 
386  // temporarily stop scan - WORKAROUND for https://github.com/zephyrproject-rtos/zephyr/issues/20660
387  // HTDBG("pausing scanning");
388  stopScanning();
389  m_context.getPlatform().getAdvertiser().stopAdvertising();
390  // HTDBG("Scanning paused");
391 
392 
393  // attempt connection, if required
394  bool ok = true;
395  if (NULL == state.connection) {
396  HTDBG(" - No existing connection. Attempting to connect");
397  // std::stringstream os;
398  // os << " - Create Param: Interval: " << zephyrinternal::defaultCreateParam.interval
399  // << ", Window: " << zephyrinternal::defaultCreateParam.window
400  // << ", Timeout: " << zephyrinternal::defaultCreateParam.timeout
401  // << " | Conn Param: Interval Min: " << zephyrinternal::BTLEConnParam->interval_min
402  // << ", Interval Max: " << zephyrinternal::defaultConnParam.interval_max
403  // << ", latency: " << zephyrinternal::defaultConnParam.latency
404  // << ", timeout: " << zephyrinternal::defaultConnParam.timeout
405  // << std::ends
406  // ;
407  // HTDBG(os.str());
408  // HTDBG(std::to_string(zephyrinternal::defaultCreateParam.interval));
409  // HTDBG(std::to_string(zephyrinternal::defaultCreateParam.window));
410  // HTDBG(std::to_string(zephyrinternal::defaultCreateParam.timeout));
411  // HTDBG(std::to_string(zephyrinternal::defaultConnParam.interval_min));
412  // HTDBG(std::to_string(zephyrinternal::defaultConnParam.interval_max));
413  // HTDBG(std::to_string(zephyrinternal::defaultConnParam.latency));
414  // HTDBG(std::to_string(zephyrinternal::defaultConnParam.timeout));
415  // HTDBG("Random address check ok?");
416  // HTDBG(bt_le_scan_random_addr_check() ? "yes" : "no");
417 
418  char addr_str[BT_ADDR_LE_STR_LEN];
419  bt_addr_le_to_str(&state.address, addr_str, sizeof(addr_str));
420  HTDBG("ADDR AS STRING in openConnection:-");
421  HTDBG(addr_str);
422 
423  state.state = BLEDeviceState::connecting; // this is used by the condition variable
424  state.remoteInstigated = false; // as we're now definitely the instigators
425  int success = bt_conn_le_create(
426  &state.address,
427  zephyrinternal::getDefaultCreateParam(),
428  zephyrinternal::getDefaultConnParam(),
429  &state.connection
430  );
431  HTDBG(" - post connection attempt");
432  if (0 != success) {
433  ok = false;
434  if (-EINVAL == success) {
435  HTDBG(" - ERROR in passed in parameters");
436  } else if (-EAGAIN == success) {
437  HTDBG(" - bt device not ready");
438  } else if (-EALREADY == success) {
439  HTDBG(" - bt device initiating")
440  } else if (-ENOMEM == success) {
441  HTDBG(" - bt connect attempt failed with default BT ID. Trying again later.");
442  // auto& device = db.device(toTarget);
443  // device.ignore(true);
444  } else if (-ENOBUFS == success) {
445  HTDBG(" - bt_hci_cmd_create has no buffers free");
446  } else if (-ECONNREFUSED == success) {
447  HTDBG(" - Connection refused");
448  } else if (-EIO == success) {
449  HTDBG(" - Low level BT HCI opcode IO failure");
450  } else {
451  HTDBG(" - Unknown error code...");
452  HTDBG(std::to_string(success));
453  }
454 
455  // Add to ignore list for now
456  // DONT DO THIS HERE - MANY REASONS IT CAN FAIL auto& device = db.device(toTarget);
457  // HTDBG(" - Ignoring following target: {}", toTarget);
458  // device.ignore(true);
459 
460  // Log last disconnected time in BLE database (records failure, allows progressive backoff)
461  auto& device = db.device(newMac); // Find by actual current physical address
462  device.state(BLEDeviceState::disconnected);
463 
464  // Immediately restart advertising on failure, but not scanning
465  // DO NOT DO THIS - may be MULTIPLE connections, one of which has succeeded, and the below interferes with gatt discovery
466  // m_context.getPlatform().getAdvertiser().startAdvertising();
467 
468  return false;
469  } else {
470  HTDBG("Zephyr waitWithTimeout for new connection");
471  // lock and wait for connection to be created
472 
473  // STD::ASYNC/MUTEX variant:-
474  // std::unique_lock<std::mutex> lk(bleInUse);
475  // connectionAvailable.wait(lk, [this] {
476  // return connectionState == BLEDeviceState::connecting;
477  // }); // BLOCKS
478  // verify connection successful
479  // connCallback(toTarget,connectionState == BLEDeviceState::connected);
480 
481  // ZEPHYR SPECIFIC VARIANT
482  uint32_t timedOut = waitWithTimeout(5'000, K_MSEC(25), [&state] {
483  return state.state == BLEDeviceState::connecting;
484  });
485  if (timedOut != 0) {
486  HTDBG("ZEPHYR WAIT TIMED OUT. Is connected?");
487  HTDBG((state.state == BLEDeviceState::connected) ? "true" : "false");
488  HTDBG(std::to_string(timedOut));
489  return false;
490  }
491  // return connectionState == BLEDeviceState::connected;
492  return state.state == BLEDeviceState::connected;
493  }
494  } else {
495  HTDBG(" - Existing connection exists! Reusing.");
496  return true;
497  }
498  }
499 
500 
501 
502  bool closeConnection(const TargetIdentifier& toTarget) override
503  {
504  HTDBG("closeConnection call for ADDR:-");
505  ConnectedDeviceState& state = findOrCreateState(toTarget);
506  char addr_str[BT_ADDR_LE_STR_LEN];
507  bt_addr_le_to_str(&state.address, addr_str, sizeof(addr_str));
508  HTDBG(addr_str);
509  // if (0 == strcmp(addr_str,"00:00:00:00:00:00 (public)")) {
510  // HTDBG("Remote address is empty. Not removing old state object.");
511  // return false; // Assume this Zephyr internal state is being managed out eventually.
512  // }
513  if (NULL != state.connection) {
514  if (state.remoteInstigated) {
515  HTDBG("Connection remote instigated - not forcing close");
516  } else if (state.inDiscovery) {
517  HTDBG("Connection in-use for service discovery - not forcing close");
518  } else if (state.isReading) {
519  HTDBG("Connection in-use for characteristic read - not forcing close");
520  } else {
521  bt_conn_disconnect(state.connection, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
522  // auto& device = db.device(toTarget);
523  // device.registerDisconnect(Date());
524  }
525  } else {
526  HTDBG("State connections is null - assuming it is closed");
527  // Can clear the remote instigated flag as they've closed the connection
528  state.remoteInstigated = false;
529  // state.isReading = false;
530  // state.readPayload.clear();
531  }
532  if (!state.remoteInstigated && !state.inDiscovery && !state.isReading) {
533  HTDBG("Removing old state connection cache object");
534  removeState(toTarget);
535  return false; // assumes we've closed it // Multi-connection tracking done elsewhere
536  }
537  HTDBG("Not removed state cache for connection. Notifying caller connection is not yet closed.");
538  return true; // remote instigated the connection - keep it open and inform caller
539  }
540 
541 
542 
544  {
545  // Print out current list of devices and their info
546  if (!connectionStates.empty()) {
547  HTDBG("Current connection states cached:-");
548  for (auto& [key,value] : connectionStates) {
549  std::string ci = " - ";
550  ci += value.target.underlyingData().hexEncodedString();
551  ci += " state: ";
552  switch (value.state) {
553  case BLEDeviceState::connected:
554  ci += "connected";
555  break;
556  case BLEDeviceState::disconnected:
557  ci += "disconnected";
558  break;
559  default:
560  ci += "connecting";
561  }
562  ci += ", connection is null: ";
563  ci += (NULL == value.connection ? "true" : "false");
564  HTDBG(ci);
565 
566  // Check connection reference is valid by address - has happened with non connectable devices (VR headset bluetooth stations)
567  bool nullBefore = (NULL == value.connection);
568  char addr_str[BT_ADDR_LE_STR_LEN];
569  bt_addr_le_to_str(&value.address, addr_str, sizeof(addr_str));
570  HTDBG(addr_str);
571  HTDBG("Looking up connection object for address just printed");
572  value.connection = bt_conn_lookup_addr_le(BT_ID_DEFAULT, &value.address);
573  if (!nullBefore && (NULL == value.connection)) {
574  HTDBG(" WARNING connection was not null, but is now we've tried to look it up again - WHY? Zephyr could not find connection by address?");
575  }
576  // If the above returns null, the next iterator will remove our state
577 
578  // Check for non null connection but disconnected state
579  if (BLEDeviceState::disconnected == value.state) {
580  HTDBG("Connection is in disconnected state. Setting state cache connection to NULL");
581  value.connection = NULL;
582  }
583  // Now check for timeout - nRF Connect doesn't cause a disconnect callback
584  if (NULL != value.connection && value.remoteInstigated) {
585  HTDBG("REMOTELY INSTIGATED OR CONNECTED DEVICE TIMED OUT");
586  auto& device = db.device(value.target);
587  // if (device.timeIntervalSinceConnected() < TimeInterval::never() &&
588  // device.timeIntervalSinceConnected() > TimeInterval::seconds(30)) {
589  // TODO verify this is true when the BLEDevice is in the state we require
590  if (device.timeIntervalSinceLastUpdate() < TimeInterval::never() &&
591  device.timeIntervalSinceLastUpdate() > TimeInterval::seconds(30)) {
592  // disconnect
593  bt_conn_disconnect(value.connection, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
594  bt_conn_unref(value.connection);
595  value.connection = NULL;
596  }
597  }
598  }
599 
600  // Do internal clean up too - remove states no longer required
601  for (auto iter = connectionStates.begin();connectionStates.end() != iter; ++iter) {
602  // We don't check for isReading or is in serviceDiscovery here as this is the catch-all, final, timeout check
603  if (NULL != iter->second.connection) {
604  // Ones that are not null, but have timed out according to BLE settings (This class doesn't get notified by BLEDatabase)
605  auto& device = db.device(iter->second.target);
606  // TODO verify this is true when the BLEDevice is in the state we require
607  // if (device.timeIntervalSinceConnected() > TimeInterval::seconds(30)) {
608  if (device.timeIntervalSinceLastUpdate() > TimeInterval::seconds(30)) {
609  bt_conn_disconnect(iter->second.connection, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
610  bt_conn_unref(iter->second.connection);
611  iter->second.connection = NULL;
612  }
613  }
614 
615  if (NULL == iter->second.connection) { // means Zephyr callbacks are finished with the connection object (i.e. disconnect was called)
616  connectionStates.erase(iter);
617  }
618  }
619  }
620 
621  // Restart scanning
622  // HTDBG("restartScanningAndAdvertising - requesting scanning and advertising restarts");
623  startScanning();
624  m_context.getPlatform().getAdvertiser().startAdvertising();
625  }
626 
627  std::optional<Activity> serviceDiscovery(Activity activity) override
628  {
629  auto currentTargetOpt = std::get<1>(activity.prerequisites.front());
630  if (!currentTargetOpt.has_value()) {
631  HTDBG("No target specified for serviceDiscovery activity. Returning.");
632  return {}; // We've been asked to connect to no specific target - not valid for Bluetooth
633  }
634  // Ensure we have a cached state (i.e. we are connected)
635  auto& state = findOrCreateState(currentTargetOpt.value());
636  if (state.state != BLEDeviceState::connected) {
637  HTDBG("Not connected to target of activity. Returning.");
638  return {};
639  }
640  if (NULL == state.connection) {
641  HTDBG("State for activity does not have a connection. Returning.");
642  return {};
643  }
644  [[maybe_unused]]
645  auto& device = db.device(currentTargetOpt.value());
646  state.inDiscovery = true;
647 
648  gatt_discover(state.connection);
649 
650  HTDBG("Zephyr waitWithTimeout for serviceDiscovery");
651  uint32_t timedOut = waitWithTimeout(5'000, K_MSEC(25), [/*&device,*/&state] () -> bool {
652  // return !device.hasServicesSet();
653  return state.inDiscovery || state.isReading;
654  });
655  HTDBG("Zephyr waitWithTimeout completed for serviceDiscovery");
656 
657  state.inDiscovery = false;
658 
659  if (0 != timedOut) {
660  HTDBG("service discovery timed out for device");
661  HTDBG(std::to_string(timedOut));
662  return {};
663  }
664  return {};
665  }
666 
667  std::optional<Activity> readPayload(Activity activity) override
668  {
669  // HTDBG("Entered readPayload activity");
670  // auto currentTargetOpt = std::get<1>(activity.prerequisites.front());
671  // if (!currentTargetOpt.has_value()) {
672  // HTDBG("No target specified for Read Payload activity. Returning.");
673  // return {}; // We've been asked to connect to no specific target - not valid for Bluetooth
674  // }
675  // // Ensure we have a cached state (i.e. we are connected)
676  // auto& state = findOrCreateState(currentTargetOpt.value());
677  // if (state.state != BLEDeviceState::connected) {
678  // HTDBG("Not connected to target of activity. Returning.");
679  // return {};
680  // }
681  // if (NULL == state.connection) {
682  // HTDBG("State for activity does not have a connection. Returning.");
683  // return {};
684  // }
685  // if (!state.isReading) {
686  // HTDBG("Reading already completed. Returning.");
687  // return {};
688  // }
689 
690  // // Note: Actual read operation started during service discovery, so just wait for it here
691 
692  // HTDBG("Zephyr waitWithTimeout for readPayload");
693  // uint32_t timedOut = waitWithTimeout(5'000, K_MSEC(25), [&state] () -> bool {
694  // return state.isReading; // service discovery or char reading not completed yet
695  // });
696  // HTDBG("Zephyr waitWithTimeout completed for readPayload");
697 
698  // // Note: Let read continue beyond the time out, so it sets isReading in the read callback once complete (not set to false here)
699 
700  // if (0 != timedOut) {
701  // HTDBG("Read Payload timed out for device");
702  // HTDBG(std::to_string(timedOut));
703  // return {};
704  // }
705  return {};
706  }
707 
708  // std::optional<Activity> immediateSend(Activity activity) override
709  // {
710  // return {};
711  // }
712  // std::optional<Activity> immediateSendAll(Activity activity) override
713  // {
714  // return {};
715  // }
716 
717 private:
718  // Zephyr OS callbacks
719  void scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type,
720  struct net_buf_simple *buf) override
721  {
722  // identify device by both MAC and potential pseudoDeviceAddress
723  BLEMacAddress bleMacAddress(addr->a.val);
724  Data advert(buf->data,buf->len);
725  auto& device = db.device(bleMacAddress,advert);
726 
727  // auto& device = db.device(target);
728  if (device.ignore()) {
729  // device.rssi(RSSI(rssi)); // TODO should we do this so our update date works and shows this as a 'live' device?
730  return;
731  }
732 
733  // // Now pass to relevant BLEDatabase API call
734  if (device.rssi().intValue() == 0) { // No RSSI yet, so must be a new device instance
735  char addr_str[BT_ADDR_LE_STR_LEN];
736  bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
737  std::string addrStr(addr_str);
738  HTDBG("New address FROM SCAN:-");
739  HTDBG(addr_str);
740  }
741 
742  // Add this RSSI reading - called at the end to ensure all other data variables set
743  device.rssi(RSSI(rssi));
744  }
745 
746  void le_param_updated(struct bt_conn *conn, uint16_t interval,
747  uint16_t latency, uint16_t timeout) override
748  {
749  HTDBG("le param updated called");
750  }
751 
752  void connected(struct bt_conn *conn, uint8_t err) override
753  {
754  HTDBG("**************** Zephyr connection callback. Mac of connected:");
755 
756  auto addr = bt_conn_get_dst(conn);
757  char addr_str[BT_ADDR_LE_STR_LEN];
758  bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
759  std::string addrStr(addr_str);
760  BLEMacAddress bleMacAddress(addr->a.val);
761  HTDBG((std::string)bleMacAddress);
762 
763  ConnectedDeviceState& state = findOrCreateStateByConnection(conn, true);
764  auto& device = db.device(bleMacAddress); // Find by actual current physical address
765 
766  if (err) { // 2 = SMP issues? StreetPass blocker on Android device perhaps. Disabled SMP use?
767  // When connecting to some devices (E.g. HTC Vive base station), you will connect BUT get an error code
768  // The below ensures that this is counted as a connection failure
769 
770  HTDBG("Connected: Error value:-");
771  HTDBG(std::to_string(err));
772  // Note: See Bluetooth Specification, Vol 2. Part D (Error codes)
773 
774  bt_conn_unref(conn);
775 
776  state.state = BLEDeviceState::disconnected;
777  state.connection = NULL;
778 
779  // Log last disconnected time in BLE database
780  device.state(BLEDeviceState::disconnected);
781 
782  // if (targetForConnection.has_value() && connCallback.has_value()) {
783  // connCallback.value()(targetForConnection.value(),false);
784  // }
785  return;
786  }
787 
788  state.connection = conn;
789  bt_addr_le_copy(&state.address,addr);
790  state.state = BLEDeviceState::connected;
791 
792  // Log last connected time in BLE database
793  device.state(BLEDeviceState::connected);
794 
795 
796  // if (targetForConnection.has_value() && connCallback.has_value()) {
797  // connCallback.value()(targetForConnection.value(),true);
798  // }
799 
800  }
801 
802  void disconnected(struct bt_conn *conn, uint8_t reason) override
803  {
804  HTDBG("********** Zephyr disconnection callback. Mac of disconnected:");
805 
806  auto addr = bt_conn_get_dst(conn);
807  char addr_str[BT_ADDR_LE_STR_LEN];
808  bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
809  std::string addrStr(addr_str);
810  BLEMacAddress bleMacAddress(addr->a.val);
811  HTDBG((std::string)bleMacAddress);
812 
813  if (reason) {
814  HTDBG("Disconnection: Reason value:-");
815  HTDBG(std::to_string(reason));
816  // Note: See Bluetooth Specification, Vol 2. Part D (Error codes)
817  // 0x20 = Unsupported LL parameter value
818  }
819 
820  // TODO log disconnection time in ble database
821 
822  bt_conn_unref(conn);
823  ConnectedDeviceState& state = findOrCreateStateByConnection(conn);
824 
825  state.state = BLEDeviceState::disconnected;
826  state.connection = NULL;
827 
828  // Log last disconnected time in BLE database
829  auto& device = db.device(bleMacAddress); // Find by actual current physical address
830  device.state(BLEDeviceState::disconnected);
831  }
832 
833  void discovery_completed_cb(struct bt_gatt_dm *dm, void *context) override
834  {
835  HTDBG("The GATT discovery procedure succeeded");
836  const struct bt_gatt_dm_attr *prev = NULL;
837  bool found = false;
838  ConnectedDeviceState& state = findOrCreateStateByConnection(bt_gatt_dm_conn_get(dm));
839  auto& device = db.device(state.target);
840  do {
841  prev = bt_gatt_dm_char_next(dm,prev);
842  if (NULL != prev) {
843  // Check for match of uuid to a herald read payload char
844  struct bt_gatt_chrc *chrc = bt_gatt_dm_attr_chrc_val(prev);
845 
846  int matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::getHeraldPayloadCharUUID()->uuid);
847  if (0 == matches) {
848  HTDBG(" - FOUND Herald read characteristic. Reading.");
849  device.payloadCharacteristic(m_context.getSensorConfiguration().payloadCharacteristicUUID);
850  // initialise payload data for this state
851  state.readPayload.clear();
852  state.isReading = true;
853 
854  // if match, for a read
855  found = true;
856  // set handles
857 
858  // TODO REFACTOR THE ACTUAL FETCHING OF PAYLOAD TO READPAYLOAD FUNCTION
859  // - Actually important, as currently a wearable will request the char multiple times from iOS before a reply is received
860  zephyrinternal::getReadParams()->single.handle = chrc->value_handle;
861  zephyrinternal::getReadParams()->single.offset = 0x0000; // gets changed on each use
862  int readErr = bt_gatt_read(bt_gatt_dm_conn_get(dm), zephyrinternal::getReadParams());
863  if (readErr) {
864  HTDBG("GATT read error: TBD");//, readErr);
865  // bt_conn_disconnect(bt_gatt_dm_conn_get(dm), BT_HCI_ERR_REMOTE_USER_TERM_CONN);
866  }
867 
868  continue; // check for other characteristics too
869  }
870  matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::getHeraldSignalAndroidCharUUID()->uuid);
871  if (0 == matches) {
872  HTDBG(" - FOUND Herald android signal characteristic. logging.");
873  device.signalCharacteristic(m_context.getSensorConfiguration().androidSignalCharacteristicUUID);
874  device.operatingSystem(BLEDeviceOperatingSystem::android);
875 
876  continue; // check for other characteristics too
877  }
878  matches = bt_uuid_cmp(chrc->uuid, &zephyrinternal::getHeraldSignalIOSCharUUID()->uuid);
879  if (0 == matches) {
880  HTDBG(" - FOUND Herald ios signal characteristic. logging.");
881  device.signalCharacteristic(m_context.getSensorConfiguration().iosSignalCharacteristicUUID);
882  device.operatingSystem(BLEDeviceOperatingSystem::ios);
883 
884  continue; // check for other characteristics too
885  }
886  // otherwise
887  char uuid_str[32];
888  bt_uuid_to_str(chrc->uuid,uuid_str,sizeof(uuid_str));
889  HTDBG(" - Char doesn't match any herald char uuid:-"); //, log_strdup(uuid_str));
890  HTDBG(uuid_str);
891  }
892  } while (NULL != prev);
893  state.inDiscovery = false;
894 
895  if (!found) {
896  HTDBG("Herald read payload char not found in herald service (weird...). Ignoring device.");
897  device.ignore(true);
898  // bt_conn_disconnect(bt_gatt_dm_conn_get(dm), BT_HCI_ERR_REMOTE_USER_TERM_CONN);
899  }
900 
901  // No it doesn't - this is safe: does ending this here break our bt_gatt_read? (as it uses that connection?)
902  int err = bt_gatt_dm_data_release(dm);
903  if (err) {
904  HTDBG("Could not release the discovery data, error code: TBD");
905  // bt_conn_disconnect(bt_gatt_dm_conn_get(dm), BT_HCI_ERR_REMOTE_USER_TERM_CONN);
906  }
907 
908  // very last action - for concurrency reasons (C++17 threading/mutex/async/future not available on Zephyr)
909  std::vector<UUID> serviceList;
910  serviceList.push_back(m_context.getSensorConfiguration().serviceUUID);
911  device.services(serviceList);
912  }
913 
914  void discovery_service_not_found_cb(struct bt_conn *conn, void *context) override
915  {
916  HTDBG("The service could not be found during the discovery. Ignoring device:");
917  ConnectedDeviceState& state = findOrCreateStateByConnection(conn);
918  HTDBG((std::string)state.target);
919 
920  auto& device = db.device(state.target);
921  std::vector<UUID> serviceList; // empty service list // TODO put other listened-for services here
922  device.services(serviceList);
923  device.ignore(true);
924  }
925 
926  void discovery_error_found_cb(struct bt_conn *conn, int err, void *context) override
927  {
928  HTDBG("The discovery procedure failed with ");
929  HTDBG(std::to_string(err));
930  // TODO decide if we should ignore the device here, or just keep trying
931  }
932 
933  uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err,
934  struct bt_gatt_read_params *params,
935  const void *data, uint16_t length) override
936  {
937  HTDBG("In gatt_read_cb");
938  // Fetch state for this element
939  ConnectedDeviceState& state = findOrCreateStateByConnection(conn);
940  if (NULL == data) {
941  HTDBG("Finished reading CHAR read payload:-");
942  HTDBG(state.readPayload.hexEncodedString());
943 
944  // Set final read payload (triggers success callback on observer)
945  db.device(state.target).payloadData(state.readPayload);
946 
947  // reset payload incase of later re-use
948  state.readPayload.clear();
949  // and set state to completed so we can clear the connection
950  state.isReading = false;
951 
952  return 0;
953  }
954 
955  state.readPayload.append((const uint8_t*)data,0,length);
956  return length;
957  }
958 
959  // std::optional<ConnectedDeviceState&> findState(const TargetIdentifier& forTarget);
960  // std::optional<ConnectedDeviceState&> findStateByConnection(struct bt_conn *conn);
961  ConnectedDeviceState& findOrCreateState(const TargetIdentifier& forTarget)
962  {
963  auto iter = connectionStates.find(forTarget);
964  if (connectionStates.end() != iter) {
965  return iter->second;
966  }
967  return connectionStates.emplace(forTarget, forTarget).first->second;
968  // return connectionStates.find(forTarget)->second;
969  }
970 
971  ConnectedDeviceState& findOrCreateStateByConnection(struct bt_conn *conn, bool remoteInstigated = false)
972  {
973  for (auto& [key, value] : connectionStates) {
974  if (value.connection == conn) {
975  return value;
976  }
977  }
978  // Create target identifier from address
979  auto addr = bt_conn_get_dst(conn);
980  BLEMacAddress bleMacAddress(addr->a.val);
981  TargetIdentifier target(bleMacAddress.underlyingData());
982  auto result = connectionStates.emplace(target, target);
983  bt_addr_le_copy(&result.first->second.address,addr);
984  result.first->second.remoteInstigated = remoteInstigated;
985  return result.first->second;
986  }
987 
988  void removeState(const TargetIdentifier& forTarget)
989  {
990  auto iter = connectionStates.find(forTarget);
991  if (connectionStates.end() != iter) {
992  connectionStates.erase(iter);
993  }
994  }
995 
996  // internal call methods
997  void startScanning()
998  {
999  if (isScanning) {
1000  return;
1001  }
1002  int err = bt_le_scan_start(zephyrinternal::getDefaultScanParam(), &zephyrinternal::scan_cb); // scan_cb linked via BT_SCAN_CB_INIT call
1003 
1004  if (0 != err) {
1005  HTDBG("Starting scanning failed");
1006  return;
1007  }
1008  isScanning = true;
1009  }
1010 
1011  void stopScanning()
1012  {
1013  if (isScanning) {
1014  isScanning = false;
1015  bt_le_scan_stop();
1016  }
1017  }
1018 
1019  void gatt_discover(struct bt_conn *conn)
1020  {
1021  HTDBG("Attempting GATT service discovery");
1022  int err;
1023 
1024  // begin introspection
1025  err = bt_gatt_dm_start(conn, &zephyrinternal::getHeraldUUID()->uuid, zephyrinternal::getDiscoveryCallbacks(), NULL);
1026  if (err) {
1027  HTDBG("could not start the discovery procedure, error code")
1028  HTDBG(std::to_string(err));
1029  bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); // ensures disconnect() called, and loop completed
1030  return;
1031  }
1032  HTDBG("Service discovery succeeded... now do something with it in the callback!");
1033  }
1034 
1035  ContextT& m_context;
1036  BluetoothStateManager& m_stateManager;
1037  PayloadDataSupplierT& m_pds;
1038  BLEDatabaseT& db;
1039 
1040  SensorDelegateSetT& delegates;
1041 
1042  std::map<TargetIdentifier,ConnectedDeviceState> connectionStates;
1043  bool isScanning;
1044 
1045  HLOGGER(ContextT);
1046 };
1047 
1048 }
1049 }
1050 
1051 #endif
Definition: ble_mac_address.h:18
Definition: bluetooth_state_manager.h:19
Dummy implementation of a ConcreteBLEReceiver that does nothing (used for testing)
Definition: concrete_ble_receiver.h:24
bool closeConnection(const TargetIdentifier &toTarget) override
Definition: concrete_ble_receiver.h:502
void restartScanningAndAdvertising() override
Definition: concrete_ble_receiver.h:543
bool openConnection(const TargetIdentifier &toTarget) override
Definition: concrete_ble_receiver.h:317
Definition: ble_protocols.h:22
The main data workhorse class of the Herald API.
Definition: data.h:33
bool uint8(std::size_t fromIndex, uint8_t &into) const noexcept
Returns whether reading a single uint8_t to into at fromIndex was successful.
Definition: data.h:324
Definition: immediate_send_data.h:13
Definition: payload_data.h:13
Definition: rssi.h:14
Definition: target_identifier.h:17
INTERNAL utility class to allow Zephyr C API to call callbacks in the Zephyr internal Context Impl cl...
Definition: zephyr_context.h:96
Contains all low-level Herald datatype implementations.
Definition: base64_string.h:14
Acts as a non-global memory arena for arbitrary classes.
Definition: aggregates.h:15
Definition: data.h:562
Definition: concrete_ble_receiver.h:57
An activity that needs to be performed due to some state being achieved in a Sensor.
Definition: activities.h:75
std::vector< Prerequisite > prerequisites
A list of non-prioritised pre-requisities (priority is taken from the priority field in Activity).
Definition: activities.h:82