herald  2.0.0
ble_concrete_database.h
1 // Copyright 2020-2021 Herald Project Contributors
2 // SPDX-License-Identifier: Apache-2.0
3 //
4 
5 #ifndef HERALD_BLE_CONCRETE_DATABASE_H
6 #define HERALD_BLE_CONCRETE_DATABASE_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_concrete.h"
13 #include "ble_protocols.h"
14 #include "bluetooth_state_manager.h"
15 #include "ble_device_delegate.h"
16 #include "filter/ble_advert_parser.h"
17 #include "../payload/payload_data_supplier.h"
18 #include "../context.h"
19 #include "../data/sensor_logger.h"
20 #include "ble_sensor_configuration.h"
21 #include "ble_coordinator.h"
22 #include "../datatype/bluetooth_state.h"
23 
24 #include <memory>
25 #include <vector>
26 #include <array>
27 #include <algorithm>
28 // #include <optional>
29 
30 namespace herald {
31 namespace ble {
32 
33 using namespace herald::datatype;
34 using namespace herald::ble::filter;
35 using namespace herald::payload;
36 
37 
40  bool operator()(const BLEDevice& a, const BLEDevice& b) {
41  return a.timeIntervalSinceLastUpdate() > b.timeIntervalSinceLastUpdate(); // opposite order
42  }
43 };
44 
45 template <typename ContextT, std::size_t MaxDevicesCached = 10>
46 class ConcreteBLEDatabase : public BLEDatabase, public BLEDeviceDelegate /*, public std::enable_shared_from_this<ConcreteBLEDatabase<ContextT>>*/ {
47 public:
48  static constexpr std::size_t MaxDevices = MaxDevicesCached;
49 
50  ConcreteBLEDatabase(ContextT& context)
51  : ctx(context),
52  delegates(),
53  devices()
54  HLOGGERINIT(context,"herald","ConcreteBLEDatabase")
55  {
56  ;
57  }
58 
59  ConcreteBLEDatabase(const ConcreteBLEDatabase& from) = delete;
61 
62  ~ConcreteBLEDatabase() = default;
63 
64  // BLE Database overrides
65 
66  void add(BLEDatabaseDelegate& delegate) override {
67  delegates.emplace_back(delegate);
68  }
69 
70  // Creation overrides
71  BLEDevice& device(const BLEMacAddress& mac, const Data& advert/*, const RSSI& rssi*/) override {
72  // Check by MAC first
73  TargetIdentifier targetIdentifier(mac.underlyingData());
74  auto results = matches([&targetIdentifier](const BLEDevice& d) {
75  return d.identifier() == targetIdentifier;
76  });
77  if (results.size() != 0) {
78  // HTDBG("DEVICE ALREADY KNOWN BY MAC");
79  // Assume advert details are known already
80  return results.front(); // TODO ensure we send back the latest, not just the first match
81  // res->rssi(rssi);
82  // return res;
83  }
84 
85  // Now check by pseudo mac
86  auto segments = BLEAdvertParser::extractSegments(advert,0);
87  // HTDBG("segments:-");
88  // HTDBG(std::to_string(segments.size()));
89  auto manuData = BLEAdvertParser::extractManufacturerData(segments);
90  auto heraldDataSegments = BLEAdvertParser::extractHeraldManufacturerData(manuData);
91  // HTDBG("herald data segments:-");
92  // HTDBG(std::to_string(heraldDataSegments.size()));
93  // auto device = db->device(bleMacAddress); // For most devices this will suffice
94 
95  // TODO Check for public herald service in ADV_IND packet - shown if an Android device, wearable or beacon in zephyr
96  // auto serviceData128 = BLEAdvertParser::extractServiceUUID128Data(segments);
97  // bool hasHeraldService = false;
98  // for (auto& service : serviceData128) {
99  // if (service.uuid == heraldUuidData) {
100  // hasHeraldService = true;
101  // HTDBG("FOUND DEVICE ADVERTISING HERALD SERVICE");
102  // device->operatingSystem(BLEDeviceOperatingSystem::android);
103  // }
104  // }
105 
106 
107  if (0 != heraldDataSegments.size()) {
108  // HTDBG("Found Herald Android pseudo device address in advert");
109  // Try to FIND by pseudo first
110  BLEMacAddress pseudo(heraldDataSegments.front());
111  auto samePseudo = matches([&pseudo](const BLEDevice& d) {
112  return d.pseudoDeviceAddress() == pseudo;
113  });
114  if (0 != samePseudo.size()) {
115  // HTDBG("FOUND EXISTING DEVICE BY PSEUDO");
116  return samePseudo.front();
117  }
118  // HTDBG("CREATING NEW DEVICE BY MAC AND PSEUDO ONLY");
119  // Now create new device with mac and pseudo
120  auto& newDevice = device(mac,pseudo);
121  assignAdvertData(newDevice,std::move(segments), manuData);
122  // newDevice->rssi(rssi);
123  return newDevice;
124  }
125 
126  // HTDBG("CREATING NEW DEVICE BY MAC ONLY");
127 
128  // Now create a device just from a mac
129  auto& newDevice = device(targetIdentifier);
130  // HTDBG("Got new device");
131  assignAdvertData(newDevice,std::move(segments), manuData);
132  // newDevice->rssi(rssi);
133  // HTDBG("Assigned advert data");
134  return newDevice;
135  }
136 
137  BLEDevice& device(const BLEMacAddress& mac, const BLEMacAddress& pseudo) override {
138  auto samePseudo = matches([&pseudo](const BLEDevice& d) {
139  return d.pseudoDeviceAddress() == pseudo;
140  });
141  if (0 == samePseudo.size()) {
142  auto& ptr = device(TargetIdentifier(pseudo.underlyingData()));
143  ptr.pseudoDeviceAddress(pseudo);
144  return ptr;
145  }
146  // get most recent and clone, then attach
147  auto comp = last_updated_descending();
148  std::sort(samePseudo.begin(),samePseudo.end(), comp); // functional style
149  BLEDevice& updatedDevice = samePseudo.front();
150  // TODO support calling card
151  // auto toShare = shareDataAcrossDevices(pseudo);
152  // if (toShare.has_value()) {
153  // updatedDevice.payloadData(toShare);
154  // }
155 
156  // Has pseudo address so must be android
157  updatedDevice.operatingSystem(BLEDeviceOperatingSystem::android);
158 
159  // register new device discovery date
160  updatedDevice.registerDiscovery(Date());
161 
162  // devices.push_back(updatedDevice);
163  for (auto& delegate : delegates) {
164  delegate.get().bleDatabaseDidCreate(updatedDevice); // may be new with a new service
165  }
166  return updatedDevice;
167  }
168 
169  BLEDevice& device(const BLEMacAddress& mac) override {
170  return device(TargetIdentifier(mac.underlyingData()));
171  }
172 
173  BLEDevice& device(const PayloadData& payloadData) override {
174  auto pti = TargetIdentifier(payloadData);
175  auto results = matches([&pti](const BLEDevice& d) {
176  return d.identifier() == pti;
177  // auto payload = d.payloadData();
178  // if (!payload.has_value()) {
179  // return false;
180  // }
181  // return (*payload)==payloadData;
182  });
183  if (results.size() != 0) {
184  return results.front(); // TODO ensure we send back the latest, not just the first match
185  }
186  BLEDevice& newDevice = devices[indexAvailable()];
187  newDevice.reset(pti,*this);
188 
189  for (auto& delegate : delegates) {
190  delegate.get().bleDatabaseDidCreate(newDevice);
191  }
192  // newDevice.payloadData(payloadData); // has to be AFTER create called
193  device(newDevice,BLEDeviceAttribute::payloadData); // moved from BLEDevice.payloadData()
194  return newDevice;
195  }
196 
197  BLEDevice& device(const TargetIdentifier& targetIdentifier) override {
198  auto results = matches([this,&targetIdentifier](const BLEDevice& d) {
199  HTDBG(" Testing existing target identifier {} against new target identifier {}",(std::string)d.identifier(),(std::string)targetIdentifier);
200  return d.identifier() == targetIdentifier;
201  });
202  if (results.size() != 0) {
203  HTDBG("Device for target identifier {} already exists",(std::string)targetIdentifier);
204  return results.front(); // TODO ensure we send back the latest, not just the first match
205  }
206  HTDBG("New target identified: {}",(std::string)targetIdentifier);
207  BLEDevice& newDevice = devices[indexAvailable()];
208  newDevice.reset(targetIdentifier,*this);
209 
210  for (auto& delegate : delegates) {
211  delegate.get().bleDatabaseDidCreate(newDevice);
212  }
213  return newDevice;
214  }
215 
216  // Introspection overrides
217  std::size_t size() const override {
218  std::size_t count = 0;
219  for (auto& d : devices) {
220  if (d.state() != BLEDeviceState::uninitialised) {
221  ++count;
222  }
223  }
224  return count;
225  }
226 
227  std::vector<std::reference_wrapper<BLEDevice>> matches(
228  const std::function<bool(const BLEDevice&)>& matcher) override {
229  std::vector<std::reference_wrapper<BLEDevice>> results;
230  // in the absence of copy_if in C++20... Just copies the pointers not the objects
231  for (auto iter = devices.begin();iter != devices.end();++iter) {
232  if (BLEDeviceState::uninitialised != iter->state() && matcher(*iter)) {
233  results.push_back(std::reference_wrapper<BLEDevice>(*iter));
234  }
235  }
236  return results;
237  }
238 
240  void remove(const TargetIdentifier& targetIdentifier) override {
241  auto found = std::find_if(devices.begin(),devices.end(),
242  [&targetIdentifier](BLEDevice& d) -> bool {
243  return d.identifier() == targetIdentifier;
244  }
245  );
246  if (found != devices.end()) {
247  BLEDevice& toRemove = *found;
248  remove(toRemove);
249  }
250  }
251 
252  // BLE Device Delegate overrides
253  void device(const BLEDevice& device, BLEDeviceAttribute didUpdate) override {
254  // Update any internal DB state as necessary (E.g. payload received and its a duplicate as mac has rotated)
255  if (BLEDeviceAttribute::payloadData == didUpdate) {
256  // check for all devices with this payload that are NOT THIS device
257  auto oldMacsForSamePayload = matches([device](auto& devRef) {
258  return devRef.identifier() != device.identifier() &&
259  devRef.payloadData().size() > 0 && devRef.payloadData() == device.payloadData();
260  });
261  for (auto& oldMacDevice : oldMacsForSamePayload) {
262  remove(oldMacDevice.get().identifier());
263  }
264  }
265 
266  // Now send update to delegates
267  for (auto& delegate : delegates) {
268  delegate.get().bleDatabaseDidUpdate(device, didUpdate); // TODO verify this is the right onward call
269  }
270  }
271 
272 private:
273  void assignAdvertData(BLEDevice& newDevice, std::vector<BLEAdvertSegment>&& toMove,
274  const std::vector<BLEAdvertManufacturerData>& manuData)
275  {
276  newDevice.advertData(std::move(toMove));
277 
278  // If it's an apple device, check to see if its on our ignore list
279  auto appleDataSegments = BLEAdvertParser::extractAppleManufacturerSegments(manuData);
280  if (0 != appleDataSegments.size()) {
281  HTDBG("Found apple device");
282  // HTDBG((std::string)mac);
283  newDevice.operatingSystem(BLEDeviceOperatingSystem::ios);
284  // TODO see if we should ignore this Apple device
285  // TODO abstract these out eventually in to BLEDevice class
286  bool ignore = false;
287  /*
288  "^10....04",
289  "^10....14",
290  "^0100000000000000000000000000000000",
291  "^05","^07","^09",
292  "^00","^1002","^06","^08","^03","^0C","^0D","^0F","^0E","^0B"
293  */
294  for (auto& segment : appleDataSegments) {
295  HTDBG(segment.data.hexEncodedString());
296  switch (segment.type) {
297  case 0x00:
298  case 0x05:
299  case 0x07:
300  case 0x09:
301  case 0x06:
302  case 0x08:
303  case 0x03:
304  case 0x0C:
305  case 0x0D:
306  case 0x0F:
307  case 0x0E:
308  case 0x0B:
309  ignore = true;
310  break;
311  case 0x10:
312  // check if second is 02
313  if (segment.data.at(0) == std::byte(0x02)) {
314  ignore = true;
315  } else {
316  // Check 3rd data bit for 14 or 04
317  if (segment.data.at(2) == std::byte(0x04) || segment.data.at(2) == std::byte(0x14)) {
318  ignore = true;
319  }
320  }
321  break;
322  default:
323  break;
324  }
325  }
326  if (ignore) {
327  HTDBG(" - Ignoring Apple device due to Apple data filter");
328  newDevice.ignore(true);
329  } else {
330  // Perform GATT service discovery to check for Herald service
331  // NOTE: Happens from Connection request (handled by BLE Coordinator)
332  HTDBG(" - Unknown apple device... Logging so we can discover services later");
333  }
334  } else {
335  // Not a Herald android or any iOS - so inspect later (beacon or wearable)
336  HTDBG("Unknown non Herald device - inspecting (might be a venue beacon or wearable)");
337  // HTDBG((std::string)mac);
338  }
339  }
340 
341  void remove(BLEDevice& toRemove) {
342  // Don't call delete/update if this device has never been initialised
343  if (toRemove.state() == BLEDeviceState::uninitialised) {
344  return;
345  }
346  toRemove.state(BLEDeviceState::uninitialised);
347  // TODO validate all other device data is reset
348  for (auto& delegate : delegates) {
349  delegate.get().bleDatabaseDidDelete(toRemove);
350  }
351  }
352 
353  std::size_t indexAvailable() noexcept {
354  for (std::size_t idx = 0;idx < devices.size();++idx) {
355  auto& device = devices[idx];
356  if (BLEDeviceState::uninitialised == device.state()) {
357  return idx;
358  }
359  }
360  // If we've got here then there is no space available
361  // Remove the oldest by lastUpdated
362  auto comp = last_updated_descending();
363  std::size_t oldestIndex = 0;
364  for (std::size_t idx = 0;idx < devices.size();++idx) {
365  if (!comp(devices[oldestIndex],devices[idx])) {
366  // new oldest
367  oldestIndex = idx;
368  }
369  }
370  // Now notify we're deleting this device
371  auto& oldest = devices[oldestIndex];
372  remove(oldest);
373  // Now re-use this reference
374  return oldestIndex;
375  }
376 
377  ContextT& ctx;
378  std::vector<std::reference_wrapper<BLEDatabaseDelegate>> delegates;
379  std::array<BLEDevice,MaxDevices> devices; // bool = in-use (not 'removed' from DB)
380 
381  HLOGGER(ContextT);
382 };
383 
384 }
385 }
386 
387 #endif
Definition: ble_database_delegate.h:15
Definition: ble_database.h:25
Definition: ble_device_delegate.h:13
Definition: ble_device.h:181
void reset(const TargetIdentifier &newID, BLEDeviceDelegate &newDelegate)
Resets the device to what it's state would be if just constructed. Allows re-use in fixed size contai...
Definition: ble_mac_address.h:18
Definition: ble_concrete_database.h:46
void remove(const TargetIdentifier &targetIdentifier) override
Cannot name a function delete in C++. remove is common.
Definition: ble_concrete_database.h:240
The main data workhorse class of the Herald API.
Definition: data.h:33
Definition: date.h:18
Definition: payload_data.h:13
Definition: target_identifier.h:17
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
Provides a callable that assists in ordering for most recently updated BLEDevice.
Definition: ble_concrete_database.h:39