bluetooth_low_energy_scan_filter.cc 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "device/bluetooth/bluetooth_low_energy_scan_filter.h"
  5. #include "base/logging.h"
  6. #include "base/memory/ptr_util.h"
  7. #include "third_party/abseil-cpp/absl/types/optional.h"
  8. namespace {
  9. // These values can be found in the "BlueZ D-Bus Advertisement Monitor API
  10. // Description": //third_party/bluez/current/doc/advertisement-monitor-api.txt
  11. constexpr int16_t kRSSIThresholdMin = -127;
  12. constexpr int16_t kRSSIThresholdMax = 20;
  13. constexpr base::TimeDelta kTimeoutMin = base::Seconds(1);
  14. constexpr base::TimeDelta kTimeoutMax = base::Seconds(300);
  15. constexpr uint8_t kPatternValueMaxLength = 31;
  16. constexpr base::TimeDelta kRSSISamplingPeriodMin = base::Milliseconds(0);
  17. constexpr base::TimeDelta kRSSISamplingPeriodMax = base::Milliseconds(254000);
  18. // These values are based on real-world testing with the goal that they will be
  19. // as high as possible without any false negatives at 1.5/6/20 feet,
  20. // respectively.
  21. constexpr int16_t kImmediateDeviceFoundRSSIThreshold = -60;
  22. constexpr int16_t kImmediateDeviceLostRSSIThreshold = -75;
  23. constexpr int16_t kNearDeviceFoundRSSIThreshold = -65;
  24. constexpr int16_t kNearDeviceLostRSSIThreshold = -80;
  25. constexpr int16_t kFarDeviceFoundRSSIThreshold = -85;
  26. constexpr int16_t kFarDeviceLostRSSIThreshold = -100;
  27. int16_t GetDeviceFoundRSSIThreshold(
  28. device::BluetoothLowEnergyScanFilter::Range range) {
  29. switch (range) {
  30. case device::BluetoothLowEnergyScanFilter::Range::kImmediate:
  31. return kImmediateDeviceFoundRSSIThreshold;
  32. case device::BluetoothLowEnergyScanFilter::Range::kNear:
  33. return kNearDeviceFoundRSSIThreshold;
  34. case device::BluetoothLowEnergyScanFilter::Range::kFar:
  35. return kFarDeviceFoundRSSIThreshold;
  36. }
  37. }
  38. int16_t GetDeviceLostRSSIThreshold(
  39. device::BluetoothLowEnergyScanFilter::Range range) {
  40. switch (range) {
  41. case device::BluetoothLowEnergyScanFilter::Range::kImmediate:
  42. return kImmediateDeviceLostRSSIThreshold;
  43. case device::BluetoothLowEnergyScanFilter::Range::kNear:
  44. return kNearDeviceLostRSSIThreshold;
  45. case device::BluetoothLowEnergyScanFilter::Range::kFar:
  46. return kFarDeviceLostRSSIThreshold;
  47. }
  48. }
  49. base::TimeDelta GetRoundedRSSISamplingPeriod(
  50. base::TimeDelta rssi_sampling_period) {
  51. // Do not round negative numbers to zero.
  52. if (rssi_sampling_period.InMilliseconds() < 0)
  53. return rssi_sampling_period;
  54. return rssi_sampling_period.CeilToMultiple(base::Milliseconds(100));
  55. }
  56. } // namespace
  57. namespace device {
  58. BluetoothLowEnergyScanFilter::Pattern::Pattern(
  59. uint8_t start_position,
  60. AdvertisementDataType data_type,
  61. const std::vector<uint8_t>& value)
  62. : start_position_(start_position),
  63. data_type_(data_type),
  64. value_(std::move(value)) {}
  65. BluetoothLowEnergyScanFilter::Pattern::Pattern(const Pattern&) = default;
  66. BluetoothLowEnergyScanFilter::Pattern::~Pattern() = default;
  67. bool BluetoothLowEnergyScanFilter::Pattern::IsValid() const {
  68. // Start position plus size of value must be less than or equal to 31 bytes.
  69. if (start_position_ + value_.size() > kPatternValueMaxLength) {
  70. DVLOG(1) << "Invalid filter configuration. Pattern start position plus "
  71. "length must be less than or equal to "
  72. << kPatternValueMaxLength << " bytes.";
  73. return false;
  74. }
  75. return true;
  76. }
  77. // static
  78. std::unique_ptr<BluetoothLowEnergyScanFilter>
  79. BluetoothLowEnergyScanFilter::Create(
  80. Range device_range,
  81. base::TimeDelta device_found_timeout,
  82. base::TimeDelta device_lost_timeout,
  83. const std::vector<Pattern>& patterns,
  84. absl::optional<base::TimeDelta> rssi_sampling_period) {
  85. return Create(GetDeviceFoundRSSIThreshold(device_range),
  86. GetDeviceLostRSSIThreshold(device_range), device_found_timeout,
  87. device_lost_timeout, patterns, rssi_sampling_period);
  88. }
  89. // static
  90. std::unique_ptr<BluetoothLowEnergyScanFilter>
  91. BluetoothLowEnergyScanFilter::Create(
  92. int16_t device_found_rssi_threshold,
  93. int16_t device_lost_rssi_threshold,
  94. base::TimeDelta device_found_timeout,
  95. base::TimeDelta device_lost_timeout,
  96. const std::vector<BluetoothLowEnergyScanFilter::Pattern>& patterns,
  97. absl::optional<base::TimeDelta> rssi_sampling_period) {
  98. // We use WrapUnique() here so that we can call the private constructor.
  99. auto filter = base::WrapUnique(new BluetoothLowEnergyScanFilter(
  100. device_found_rssi_threshold, device_lost_rssi_threshold,
  101. device_found_timeout, device_lost_timeout, patterns,
  102. rssi_sampling_period));
  103. if (!filter->IsValid()) {
  104. return nullptr;
  105. }
  106. return filter;
  107. }
  108. BluetoothLowEnergyScanFilter::BluetoothLowEnergyScanFilter(
  109. int16_t device_found_rssi_threshold,
  110. int16_t device_lost_rssi_threshold,
  111. base::TimeDelta device_found_timeout,
  112. base::TimeDelta device_lost_timeout,
  113. std::vector<Pattern> patterns,
  114. absl::optional<base::TimeDelta> rssi_sampling_period)
  115. : device_found_rssi_threshold_(device_found_rssi_threshold),
  116. device_lost_rssi_threshold_(device_lost_rssi_threshold),
  117. device_found_timeout_(device_found_timeout),
  118. device_lost_timeout_(device_lost_timeout),
  119. patterns_(patterns),
  120. rssi_sampling_period_(rssi_sampling_period) {
  121. // |rssi_sampling_period_| needs to be rounded because the BlueZ API only
  122. // supports 100ms increments.
  123. if (rssi_sampling_period_.has_value()) {
  124. rssi_sampling_period_ =
  125. GetRoundedRSSISamplingPeriod(rssi_sampling_period_.value());
  126. }
  127. }
  128. BluetoothLowEnergyScanFilter::~BluetoothLowEnergyScanFilter() = default;
  129. bool BluetoothLowEnergyScanFilter::IsValid() const {
  130. if (device_found_rssi_threshold_ < kRSSIThresholdMin ||
  131. device_lost_rssi_threshold_ < kRSSIThresholdMin) {
  132. DVLOG(1)
  133. << "Invalid filter configuration. RSSI thresholds must be larger than "
  134. << kRSSIThresholdMin << ".";
  135. return false;
  136. }
  137. if (device_found_rssi_threshold_ > kRSSIThresholdMax ||
  138. device_lost_rssi_threshold_ > kRSSIThresholdMax) {
  139. DVLOG(1)
  140. << "Invalid filter configuration. RSSI thresholds must be smaller than "
  141. << kRSSIThresholdMax << ".";
  142. return false;
  143. }
  144. if (device_found_rssi_threshold_ <= device_lost_rssi_threshold_) {
  145. DVLOG(1) << "Invalid filter configuration. Device found RSSI threshold "
  146. "must be larger than device lost RSSI threshold.";
  147. return false;
  148. }
  149. if (device_found_timeout_ < kTimeoutMin ||
  150. device_lost_timeout_ < kTimeoutMin) {
  151. DVLOG(1) << "Invalid filter configuration. Timeouts must be larger than "
  152. << kTimeoutMin << ".";
  153. return false;
  154. }
  155. if (device_found_timeout_ > kTimeoutMax ||
  156. device_lost_timeout_ > kTimeoutMax) {
  157. DVLOG(1) << "Invalid filter configuration. Timeouts must be smaller than "
  158. << kTimeoutMax << ".";
  159. return false;
  160. }
  161. if (patterns_.empty()) {
  162. DVLOG(1) << "Invalid filter configuration. Patterns must not be empty.";
  163. return false;
  164. }
  165. for (const auto& pattern : patterns_) {
  166. if (!pattern.IsValid()) {
  167. return false;
  168. }
  169. }
  170. if (rssi_sampling_period_.has_value() &&
  171. (rssi_sampling_period_.value() < kRSSISamplingPeriodMin ||
  172. rssi_sampling_period_.value() > kRSSISamplingPeriodMax)) {
  173. DVLOG(1) << "Invalid RSSI sampling rate: "
  174. << rssi_sampling_period_.value().InMilliseconds()
  175. << " ms . RSSI sampling rate must be between "
  176. << kRSSISamplingPeriodMin.InMilliseconds() << " ms and "
  177. << kRSSISamplingPeriodMax.InMilliseconds() << " ms, inclusive.";
  178. return false;
  179. }
  180. return true;
  181. }
  182. } // namespace device