Refactor BleAdvertisementWatcher.

PiperOrigin-RevId: 732976572
This commit is contained in:
Francis Tsui
2025-03-03 10:39:48 -08:00
committed by Copybara-Service
parent fe6ad715ac
commit 8d9d310a07
3 changed files with 372 additions and 193 deletions
+243 -187
View File
@@ -16,6 +16,7 @@
#include <algorithm>
#include <array>
#include <chrono> // NOLINT
#include <cstddef>
#include <cstdint>
#include <exception>
@@ -54,6 +55,7 @@
#include "winrt/Windows.Devices.Bluetooth.Advertisement.h"
#include "winrt/Windows.Devices.Bluetooth.h"
#include "winrt/Windows.Foundation.Collections.h"
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.Storage.Streams.h" // NOLINT(misc-include-cleaner)
namespace nearby {
@@ -95,7 +97,9 @@ using ::winrt::Windows::Devices::Bluetooth::Advertisement::
using ::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEScanningMode;
using ::winrt::Windows::Foundation::TimeSpan;
using ::winrt::Windows::Foundation::TypedEventHandler;
using ::winrt::Windows::Storage::Streams::Buffer;
using ::winrt::Windows::Storage::Streams::ByteOrder;
using ::winrt::Windows::Storage::Streams::DataReader;
using ::winrt::Windows::Storage::Streams::DataWriter;
@@ -123,14 +127,160 @@ static constexpr uint64_t kGenerateSessionIdRetryLimit = 3;
static constexpr uint64_t kFailedGenerateSessionId = 0;
constexpr absl::Duration kMediumTimeout = absl::Milliseconds(500);
constexpr int kMediumCheckIntervalInMills = 50;
constexpr absl::Duration kMediumCheckInterval = absl::Milliseconds(50);
// Remove lost/unused peripherals after a timeout.
constexpr absl::Duration kPeripheralExpiryTime = absl::Minutes(15);
// Prevent too frequent cleanup tasks.
constexpr absl::Duration kMaxPeripheralCleanupFrequency = absl::Minutes(3);
// The most significant bits and the least significant bits or Bluetooth Base
// UUID.
// See Bluetooth Core Specification 6.0 Vol.3, Part B, Section 2.5.1
const std::uint64_t kBluetoothBaseUuidMsb = 0x0000000000001000;
const std::uint64_t kBluetoothBaseUuidLsb = 0x800000805F9B34FB;
// Mask for UUID16 bits.
const std::uint64_t kBluetoothBaseUuid16MsbMask = 0x0000FFFF00000000;
// Service Data - 16-bit UUID. From Bluetooth Assigned Numbers Section 2.3.
constexpr uint8_t kUuid16ServiceDataType = 0x16;
} // namespace
BleV2Medium::AdvertisementWatcher::~AdvertisementWatcher() { Stop(); }
void BleV2Medium::AdvertisementWatcher::Initialize(
const TypedEventHandler<BluetoothLEAdvertisementWatcher,
BluetoothLEAdvertisementReceivedEventArgs>&
received_handler,
const TypedEventHandler<BluetoothLEAdvertisementWatcher,
BluetoothLEAdvertisementWatcherStoppedEventArgs>&
stopped_handler) {
absl::MutexLock lock(&mutex_);
try {
watcher_token_ = watcher_.Stopped(stopped_handler);
advertisement_received_token_ = watcher_.Received(received_handler);
watcher_.AllowExtendedAdvertisements(true);
// Active mode indicates that scan request packets will be sent to query
// for Scan Response
watcher_.ScanningMode(BluetoothLEScanningMode::Active);
::winrt::Windows::Devices::Bluetooth::BluetoothSignalStrengthFilter filter;
filter.SamplingInterval(TimeSpan(std::chrono::seconds(2)));
watcher_.SignalStrengthFilter(filter);
initialized_ = true;
} catch (std::exception exception) {
LOG(ERROR) << __func__
<< ": Exception on BLE scan initialize: " << exception.what();
} catch (const winrt::hresult_error& ex) {
LOG(ERROR) << __func__
<< ": Exception on BLE scan initialize: " << ex.code() << ": "
<< winrt::to_string(ex.message());
} catch (...) {
LOG(ERROR) << __func__ << ": Unknown exception on BLE scan initialize.";
}
}
void BleV2Medium::AdvertisementWatcher::InitializeWithServiceFilter(
uint16_t service_uuid,
const TypedEventHandler<BluetoothLEAdvertisementWatcher,
BluetoothLEAdvertisementReceivedEventArgs>&
received_handler,
const TypedEventHandler<BluetoothLEAdvertisementWatcher,
BluetoothLEAdvertisementWatcherStoppedEventArgs>&
stopped_handler) {
absl::MutexLock lock(&mutex_);
try {
watcher_token_ = watcher_.Stopped(stopped_handler);
advertisement_received_token_ = watcher_.Received(received_handler);
watcher_.AllowExtendedAdvertisements(true);
// Active mode indicates that scan request packets will be sent to query
// for Scan Response
watcher_.ScanningMode(BluetoothLEScanningMode::Active);
BluetoothLEAdvertisementDataSection data_section;
data_section.DataType(kUuid16ServiceDataType);
DataWriter data_writer; // NOLINT(misc-include-cleaner)
data_writer.ByteOrder(ByteOrder::LittleEndian);
data_writer.WriteInt16(service_uuid);
data_section.Data(data_writer.DetachBuffer());
BluetoothLEAdvertisement advertisement;
advertisement.DataSections().Append(data_section);
BluetoothLEAdvertisementFilter advertisement_filter;
advertisement_filter.Advertisement(advertisement);
watcher_.AdvertisementFilter(advertisement_filter);
initialized_ = true;
} catch (std::exception exception) {
LOG(ERROR) << __func__
<< ": Exception on BLE scan initialize: " << exception.what();
} catch (const winrt::hresult_error& ex) {
LOG(ERROR) << __func__
<< ": Exception on BLE scan initialize, hresult: " << ex.code()
<< ": " << winrt::to_string(ex.message());
} catch (...) {
LOG(ERROR) << __func__ << ": Unknown exception on BLE scan initialize.";
}
}
bool BleV2Medium::AdvertisementWatcher::Start() {
absl::MutexLock lock(&mutex_);
if (!initialized_) {
LOG(ERROR) << __func__ << ": Failed to start BLE watcher: not initialized.";
return false;
}
try {
watcher_.Start();
// Wait for watcher to transition to Started state.
// Any state other than Started or Created is considered a failure.
absl::Duration wait_duration;
while (wait_duration < kMediumTimeout &&
watcher_.Status() ==
BluetoothLEAdvertisementWatcherStatus::Created) {
absl::SleepFor(kMediumCheckInterval);
wait_duration += kMediumCheckInterval;
}
BluetoothLEAdvertisementWatcherStatus status = watcher_.Status();
if (status != BluetoothLEAdvertisementWatcherStatus::Started) {
LOG(ERROR) << __func__ << ": Failed to start BLE watcher: "
<< static_cast<int>(status);
return false;
}
LOG(INFO) << __func__ << ": BLE scanning started.";
} catch (std::exception exception) {
LOG(ERROR) << __func__
<< ": Exception on BLE scan start: " << exception.what();
} catch (const winrt::hresult_error& ex) {
LOG(ERROR) << __func__
<< ": Exception on BLE scan start, hresult: " << ex.code()
<< ": " << winrt::to_string(ex.message());
} catch (...) {
LOG(ERROR) << __func__ << ": Unknown exception on BLE scan start.";
}
return true;
}
void BleV2Medium::AdvertisementWatcher::Stop() {
absl::MutexLock lock(&mutex_);
try {
watcher_.Stop();
if (watcher_token_) {
watcher_.Stopped(watcher_token_);
}
if (advertisement_received_token_) {
watcher_.Received(advertisement_received_token_);
}
initialized_ = false;
} catch (std::exception exception) {
LOG(ERROR) << __func__
<< ": Exception on BLE scan stop: " << exception.what();
} catch (const winrt::hresult_error& ex) {
LOG(ERROR) << __func__
<< ": Exception on BLE scan stop, hresult: " << ex.code() << ": "
<< winrt::to_string(ex.message());
} catch (...) {
LOG(ERROR) << __func__ << ": Unknown exception on BLE scan stop.";
}
}
BleV2Medium::BleV2Medium(api::BluetoothAdapter& adapter)
: adapter_(dynamic_cast<BluetoothAdapter*>(&adapter)) {}
@@ -227,90 +377,61 @@ std::unique_ptr<BleV2Medium::AdvertisingSession> BleV2Medium::StartAdvertising(
});
}
std::unique_ptr<BleV2Medium::AdvertisementWatcher>
BleV2Medium::CreateBleWatcher(uint16_t service_uuid) {
auto watcher = std::make_unique<AdvertisementWatcher>();
watcher->InitializeWithServiceFilter(
service_uuid, {this, &BleV2Medium::AdvertisementReceivedHandler},
{this, &BleV2Medium::WatcherHandler});
if (!watcher->Start()) {
return nullptr;
}
LOG(INFO) << __func__ << ": BLE scanning started for service uuid "
<< absl::StrCat(absl::Hex(service_uuid));
return watcher;
}
bool BleV2Medium::StartScanning(const Uuid& service_uuid,
TxPowerLevel tx_power_level,
ScanCallback callback) {
absl::MutexLock lock(&mutex_);
LOG(INFO) << __func__ << ": service UUID: " << std::string(service_uuid)
<< ", TxPowerLevel: " << TxPowerLevelToName(tx_power_level);
try {
if (!adapter_->IsEnabled()) {
LOG(WARNING) << __func__
<< "BLE cannot start scanning because the "
"Bluetooth adapter is not enabled.";
return false;
}
if (is_watcher_started_) {
LOG(WARNING) << __func__
<< ": BLE cannot start to scan again when it is running.";
return false;
}
service_uuid_ = service_uuid;
tx_power_level_ = tx_power_level;
scan_callback_ = std::move(callback);
watcher_ = BluetoothLEAdvertisementWatcher();
watcher_token_ = watcher_.Stopped({this, &BleV2Medium::WatcherHandler});
advertisement_received_token_ =
watcher_.Received({this, &BleV2Medium::AdvertisementReceivedHandler});
if (adapter_->IsExtendedAdvertisingSupported()) {
watcher_.AllowExtendedAdvertisements(true);
}
// Active mode indicates that scan request packets will be sent to query for
// Scan Response
watcher_.ScanningMode(BluetoothLEScanningMode::Active);
BluetoothLEAdvertisementDataSection data_section;
data_section.DataType(0x16);
DataWriter data_writer; // NOLINT(misc-include-cleaner)
std::array<char, 16> service_id_data = service_uuid_.data();
data_writer.WriteByte(service_id_data[3] & 0xff);
data_writer.WriteByte(service_id_data[2] & 0xff);
data_section.Data(data_writer.DetachBuffer());
BluetoothLEAdvertisement advertisement;
advertisement.DataSections().Append(data_section);
BluetoothLEAdvertisementFilter advertisement_filter;
advertisement_filter.Advertisement(advertisement);
watcher_.AdvertisementFilter(advertisement_filter);
watcher_.Start();
// Wait for the watcher to start.
int wait_milliseconds = 0;
while (watcher_.Status() !=
BluetoothLEAdvertisementWatcherStatus::Started) {
absl::SleepFor(absl::Milliseconds(kMediumCheckIntervalInMills));
wait_milliseconds += kMediumCheckIntervalInMills;
if (absl::Milliseconds(wait_milliseconds) > kMediumTimeout) {
LOG(ERROR) << __func__ << ": Failed to start BLE scan due to timeout..";
watcher_.Stopped(watcher_token_);
watcher_.Received(advertisement_received_token_);
watcher_ = nullptr;
return false;
}
}
is_watcher_started_ = true;
LOG(INFO) << __func__ << ": BLE scanning started.";
return true;
} catch (std::exception exception) {
LOG(ERROR) << __func__
<< ": Exception to start BLE scanning: " << exception.what();
return false;
} catch (const winrt::hresult_error& ex) {
LOG(ERROR) << __func__ << ": Exception to start BLE scanning: " << ex.code()
<< ": " << winrt::to_string(ex.message());
return false;
} catch (...) {
LOG(ERROR) << __func__ << ": Unknown exception.";
if (!adapter_->IsEnabled()) {
LOG(WARNING) << __func__
<< "BLE cannot start scanning because the "
"Bluetooth adapter is not enabled.";
return false;
}
if (!watchers_.empty()) {
LOG(WARNING) << __func__
<< ": BLE cannot start to scan again when it is running.";
return false;
}
service_uuid_ = service_uuid;
tx_power_level_ = tx_power_level;
scan_callback_ = std::move(callback);
// This assumes that service_uuid has a valid UUID16 alias.
// Verify that service_uuid in in valid range.
DCHECK_EQ(service_uuid_.GetMostSigBits() & ~kBluetoothBaseUuid16MsbMask,
kBluetoothBaseUuidMsb);
DCHECK_EQ(service_uuid_.GetLeastSigBits(), kBluetoothBaseUuidLsb);
service_uuid16_ =
(service_uuid_.GetMostSigBits() & kBluetoothBaseUuid16MsbMask) >> 32;
std::unique_ptr<AdvertisementWatcher> watcher =
CreateBleWatcher(service_uuid16_);
if (watcher == nullptr) {
return false;
}
watchers_.push_back(std::move(watcher));
LOG(INFO) << __func__ << ": BLE scanning started.";
return true;
}
std::unique_ptr<BleV2Medium::ScanningSession> BleV2Medium::StartScanning(
@@ -325,43 +446,16 @@ std::unique_ptr<BleV2Medium::ScanningSession> BleV2Medium::StartScanning(
"Bluetooth adapter is not enabled.";
return nullptr;
}
if (!is_watcher_started_) {
if (watchers_.empty()) {
LOG(WARNING) << __func__ << ": Starting BLE Scanning.";
try {
watcher_ = BluetoothLEAdvertisementWatcher();
watcher_token_ = watcher_.Stopped({this, &BleV2Medium::WatcherHandler});
advertisement_received_token_ =
watcher_.Received({this, &BleV2Medium::AdvertisementFoundHandler});
if (adapter_->IsExtendedAdvertisingSupported()) {
watcher_.AllowExtendedAdvertisements(true);
} else {
watcher_.AllowExtendedAdvertisements(false);
}
// Active mode indicates that scan request packets will be sent to query
// for Scan Response
watcher_.ScanningMode(BluetoothLEScanningMode::Active);
::winrt::Windows::Devices::Bluetooth::BluetoothSignalStrengthFilter
filter;
filter.SamplingInterval(TimeSpan(std::chrono::seconds(2)));
watcher_.SignalStrengthFilter(filter);
watcher_.Start();
is_watcher_started_ = true;
LOG(INFO) << __func__ << ": BLE scanning started.";
} catch (std::exception exception) {
LOG(ERROR) << __func__
<< ": Exception to start BLE scanning: " << exception.what();
return nullptr;
} catch (const winrt::hresult_error& ex) {
LOG(ERROR) << __func__
<< ": Exception to start BLE scanning: " << ex.code() << ": "
<< winrt::to_string(ex.message());
return nullptr;
} catch (...) {
LOG(ERROR) << __func__ << ": Unknown exception.";
auto watcher = std::make_unique<AdvertisementWatcher>();
watcher->Initialize({this, &BleV2Medium::AdvertisementFoundHandler},
{this, &BleV2Medium::WatcherHandler});
if (!watcher->Start()) {
return nullptr;
}
watchers_.push_back(std::move(watcher));
LOG(INFO) << __func__ << ": BLE scanning started.";
} else {
LOG(WARNING) << __func__ << ": BLE Scanning already started.";
}
@@ -401,8 +495,7 @@ std::unique_ptr<BleV2Medium::ScanningSession> BleV2Medium::StartScanning(
try {
LOG(INFO)
<< "No more scan sessions, stopping Ble scanning.";
watcher_.Stop();
is_watcher_started_ = false;
watchers_.clear();
LOG(INFO) << "Ble stoped scanning successfully.";
} catch (std::exception exception) {
@@ -513,59 +606,23 @@ bool BleV2Medium::StopScanning() {
absl::MutexLock lock(&mutex_);
LOG(INFO) << __func__ << ": BLE StopScanning: service_uuid: "
<< std::string(service_uuid_);
try {
if (!adapter_->IsEnabled()) {
LOG(WARNING) << "BLE cannot stop scanning because the "
"bluetooth adapter is not enabled.";
return false;
}
if (!is_watcher_started_) {
LOG(WARNING) << "BLE scanning is not running.";
return false;
}
watcher_.Stop();
// Wait for the watcher to stop.
int wait_milliseconds = 0;
while (watcher_.Status() !=
BluetoothLEAdvertisementWatcherStatus::Stopped) {
absl::SleepFor(absl::Milliseconds(kMediumCheckIntervalInMills));
wait_milliseconds += kMediumCheckIntervalInMills;
if (absl::Milliseconds(wait_milliseconds) > kMediumTimeout) {
LOG(ERROR) << __func__ << ": Failed to stop BLE scan due to timeout.";
watcher_.Stopped(watcher_token_);
watcher_.Received(advertisement_received_token_);
watcher_ = nullptr;
is_watcher_started_ = false;
return false;
}
}
watcher_.Stopped(watcher_token_);
watcher_.Received(advertisement_received_token_);
watcher_ = nullptr;
is_watcher_started_ = false;
LOG(ERROR) << "Windows Ble stoped scanning successfully for service UUID:"
<< std::string(service_uuid_);
return true;
} catch (std::exception exception) {
LOG(ERROR) << __func__
<< ": Exception to stop BLE scanning: " << exception.what();
return false;
} catch (const winrt::hresult_error& ex) {
LOG(ERROR) << __func__ << ": Exception to stop BLE scanning: " << ex.code()
<< ": " << winrt::to_string(ex.message());
return false;
} catch (...) {
LOG(ERROR) << __func__ << ": Unknown exception.";
<< absl::StrCat(absl::Hex(service_uuid16_));
if (!adapter_->IsEnabled()) {
LOG(WARNING) << "BLE cannot stop scanning because the "
"bluetooth adapter is not enabled.";
return false;
}
if (watchers_.empty()) {
LOG(WARNING) << "BLE scanning is not running.";
return false;
}
watchers_.clear();
LOG(ERROR) << "Windows Ble stoped scanning successfully for service UUID: "
<< absl::StrCat(absl::Hex(service_uuid16_));
return true;
}
std::unique_ptr<api::ble_v2::BleServerSocket> BleV2Medium::OpenServerSocket(
@@ -668,7 +725,8 @@ bool BleV2Medium::StartBleAdvertising(
}
BluetoothLEAdvertisementDataSection ble_service_data =
BluetoothLEAdvertisementDataSection(0x16, data_writer.DetachBuffer());
BluetoothLEAdvertisementDataSection(kUuid16ServiceDataType,
data_writer.DetachBuffer());
data_sections.Append(ble_service_data);
}
@@ -703,12 +761,12 @@ bool BleV2Medium::StartBleAdvertising(
publisher_.Start();
// Wait for the publisher to start.
int wait_milliseconds = 0;
absl::Duration wait_duration;
while (publisher_.Status() !=
BluetoothLEAdvertisementPublisherStatus::Started) {
absl::SleepFor(absl::Milliseconds(kMediumCheckIntervalInMills));
wait_milliseconds += kMediumCheckIntervalInMills;
if (absl::Milliseconds(wait_milliseconds) > kMediumTimeout) {
absl::SleepFor(kMediumCheckInterval);
wait_duration += kMediumCheckInterval;
if (wait_duration > kMediumTimeout) {
LOG(ERROR) << __func__
<< ": BLE advertising failed to start due to timeout.";
publisher_.StatusChanged(publisher_token_);
@@ -763,12 +821,12 @@ bool BleV2Medium::StopBleAdvertising() {
publisher_.Stop();
// Wait for the publisher to stop.
int wait_milliseconds = 0;
absl::Duration wait_duration;
while (publisher_.Status() !=
BluetoothLEAdvertisementPublisherStatus::Stopped) {
absl::SleepFor(absl::Milliseconds(kMediumCheckIntervalInMills));
wait_milliseconds += kMediumCheckIntervalInMills;
if (absl::Milliseconds(wait_milliseconds) > kMediumTimeout) {
absl::SleepFor(kMediumCheckInterval);
wait_duration += kMediumCheckInterval;
if (wait_duration > kMediumTimeout) {
LOG(ERROR) << __func__
<< ": BLE advertising failed to stop due to timeout.";
publisher_.StatusChanged(publisher_token_);
@@ -1042,18 +1100,16 @@ void BleV2Medium::AdvertisementReceivedHandler(
// 0x16 Service Data) has been received in the handler
BluetoothLEAdvertisement advertisement = args.Advertisement();
std::array<char, 16> service_id_data = service_uuid_.data();
for (BluetoothLEAdvertisementDataSection service_data :
advertisement.GetSectionsByType(0x16)) {
// Parse Advertisement Data for Section 0x16 (Service Data)
advertisement.GetSectionsByType(kUuid16ServiceDataType)) {
// Parse Advertisement Data for Section 0x16 (16bit uuid Service Data)
DataReader data_reader = DataReader::FromBuffer(service_data.Data());
data_reader.ByteOrder(ByteOrder::LittleEndian);
// Discard the first 2 bytes of Service Uuid in Service Data
uint8_t first_byte = data_reader.ReadByte();
uint8_t second_byte = data_reader.ReadByte();
uint16_t service_uuid16 = data_reader.ReadUInt16();
if (first_byte == (service_id_data[3] & 0xff) &&
second_byte == (service_id_data[2] & 0xff)) {
if (service_uuid16 == service_uuid16_) {
std::string data;
uint8_t unconsumed_buffer_length = data_reader.UnconsumedBufferLength();
@@ -1063,7 +1119,7 @@ void BleV2Medium::AdvertisementReceivedHandler(
ByteArray advertisement_data(data);
VLOG(1) << "Nearby BLE Medium " << service_uuid_.Get16BitAsString()
VLOG(1) << "Nearby BLE Medium " << absl::StrCat(absl::Hex(service_uuid16))
<< " Advertisement discovered. "
"0x16 Service data: advertisement bytes= 0x"
<< absl::BytesToHexString(advertisement_data.AsStringView())
@@ -1126,18 +1182,18 @@ void BleV2Medium::AdvertisementFoundHandler(
// Construct BleAdvertisementData from the windows Advertisement.
api::ble_v2::BleAdvertisementData ble_advertisement_data;
for (BluetoothLEAdvertisementDataSection service_data :
advertisement.GetSectionsByType(0x16)) {
advertisement.GetSectionsByType(kUuid16ServiceDataType)) {
// Parse Advertisement Data for Section 0x16 (Service Data)
DataReader data_reader = DataReader::FromBuffer(service_data.Data());
data_reader.ByteOrder(ByteOrder::LittleEndian);
// Discard the first 2 bytes of Service Uuid in Service Data
uint8_t first_byte = data_reader.ReadByte();
uint8_t second_byte = data_reader.ReadByte();
uint16_t service_uuid16 = data_reader.ReadUInt16();
for (auto service_uuid : service_uuid_list) {
std::array<char, 16> service_id_data = service_uuid.data();
if (first_byte == (service_id_data[3] & 0xff) &&
second_byte == (service_id_data[2] & 0xff)) {
uint16_t uuid16 =
(service_uuid.GetMostSigBits() & kBluetoothBaseUuid16MsbMask) >> 32;
if (uuid16 == service_uuid16) {
std::string data;
uint8_t unconsumed_buffer_length = data_reader.UnconsumedBufferLength();
@@ -19,6 +19,7 @@
#include <cstdint>
#include <memory>
#include <string>
#include <vector>
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
@@ -90,6 +91,60 @@ class BleV2Medium : public api::ble_v2::BleMedium {
ABSL_LOCKS_EXCLUDED(mutex_);
private:
friend class BleV2MediumTest;
// A wrapper class for BluetoothLEAdvertisementWatcher.
class AdvertisementWatcher {
public:
AdvertisementWatcher() = default;
~AdvertisementWatcher();
// Initializes an advertisement watcher with no service uuid filter.
void Initialize(
const winrt::Windows::Foundation::TypedEventHandler<
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher,
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementReceivedEventArgs>& received_handler,
const winrt::Windows::Foundation::TypedEventHandler<
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher,
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcherStoppedEventArgs>&
stopped_handler) ABSL_LOCKS_EXCLUDED(mutex_);
// Initializes an advertisement watcher with a filter for UUID16 service
// data for `service_uuid`.
void InitializeWithServiceFilter(
uint16_t service_uuid,
const winrt::Windows::Foundation::TypedEventHandler<
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher,
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementReceivedEventArgs>& received_handler,
const winrt::Windows::Foundation::TypedEventHandler<
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher,
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcherStoppedEventArgs>&
stopped_handler) ABSL_LOCKS_EXCLUDED(mutex_);
// Start scanning for advertisements.
// Returns true if the watcher is successfully started.
bool Start() ABSL_LOCKS_EXCLUDED(mutex_);
// Stop scanning for advertisements.
void Stop() ABSL_LOCKS_EXCLUDED(mutex_);
private:
absl::Mutex mutex_;
bool initialized_ ABSL_GUARDED_BY(mutex_)= false;
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher watcher_ ABSL_GUARDED_BY(mutex_);
::winrt::event_token watcher_token_ ABSL_GUARDED_BY(mutex_);
::winrt::event_token advertisement_received_token_ ABSL_GUARDED_BY(mutex_);
};
bool StartBleAdvertising(
const api::ble_v2::BleAdvertisementData& advertising_data,
api::ble_v2::AdvertiseParameters advertising_parameters)
@@ -134,13 +189,18 @@ class BleV2Medium : public api::ble_v2::BleMedium {
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
void RemoveExpiredPeripherals() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
std::unique_ptr<BleV2Medium::AdvertisementWatcher> CreateBleWatcher(
uint16_t service_uuid);
absl::Mutex mutex_;
BluetoothAdapter* const adapter_;
Uuid service_uuid_;
uint16_t service_uuid16_ = 0;
api::ble_v2::TxPowerLevel tx_power_level_;
ScanCallback scan_callback_;
std::vector<std::unique_ptr<AdvertisementWatcher>> watchers_
ABSL_GUARDED_BY(mutex_);
// std::map<Uuid, std::map<uint64_t, ScanningCallback>>
absl::flat_hash_map<Uuid, absl::flat_hash_map<uint64_t, ScanningCallback>>
@@ -150,17 +210,11 @@ class BleV2Medium : public api::ble_v2::BleMedium {
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementPublisher publisher_ ABSL_GUARDED_BY(mutex_) =
nullptr;
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher watcher_ ABSL_GUARDED_BY(mutex_) =
nullptr;
bool is_ble_publisher_started_ ABSL_GUARDED_BY(mutex_) = false;
bool is_gatt_publisher_started_ ABSL_GUARDED_BY(mutex_) = false;
bool is_watcher_started_ ABSL_GUARDED_BY(mutex_) = false;
::winrt::event_token publisher_token_ ABSL_GUARDED_BY(mutex_);
::winrt::event_token watcher_token_ ABSL_GUARDED_BY(mutex_);
::winrt::event_token advertisement_received_token_ ABSL_GUARDED_BY(mutex_);
BleGattServer* ble_gatt_server_ = nullptr;
// Map to protect the pointer for BlePeripheral because
@@ -14,17 +14,86 @@
#include "internal/platform/implementation/windows/ble_v2.h"
#include <cstddef>
#include <cstdint>
#include <string>
#include <memory>
#include <utility>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
#include "absl/synchronization/notification.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/implementation/windows/bluetooth_adapter.h"
#include "internal/platform/uuid.h"
namespace nearby {
namespace windows {
namespace {
using ::testing::Return;
constexpr uint64_t kTestServiceUuidMsb = 0x0000ABCD00001000;
constexpr uint64_t kTestServiceUuidLsb = 0x800000805F9B34FB;
constexpr Uuid kTestServiceUuid(kTestServiceUuidMsb, kTestServiceUuidLsb);
class MockBluetoothAdapter : public BluetoothAdapter {
public:
MOCK_METHOD(bool, SetStatus, (Status status), (override));
MOCK_METHOD(bool, IsEnabled, (), (const, override));
MOCK_METHOD(ScanMode, GetScanMode, (), (const, override));
MOCK_METHOD(bool, SetScanMode, (ScanMode scan_mode), (override));
MOCK_METHOD(std::string, GetName, (), (const, override));
MOCK_METHOD(bool, SetName, (absl::string_view name), (override));
MOCK_METHOD(bool, SetName, (absl::string_view name, bool persist),
(override));
MOCK_METHOD(std::string, GetMacAddress, (), (const, override));
};
} // namespace
class BleV2MediumTest : public ::testing::Test {
protected:
BleV2MediumTest() {
blev2_medium_ = std::make_unique<BleV2Medium>(bluetooth_adapter_);
}
protected:
size_t GetWatchersCount() {
absl::MutexLock lock(&blev2_medium_->mutex_);
return blev2_medium_->watchers_.size();
}
MockBluetoothAdapter bluetooth_adapter_;
std::unique_ptr<BleV2Medium> blev2_medium_;
};
namespace {
TEST_F(BleV2MediumTest, StartScanningAdapterDisabled) {
EXPECT_CALL(bluetooth_adapter_, IsEnabled()).WillOnce(Return(false));
Uuid service_uuid = kTestServiceUuid;
api::ble_v2::BleMedium::ScanCallback callback;
EXPECT_EQ(false, blev2_medium_->StartScanning(
service_uuid, api::ble_v2::TxPowerLevel::kHigh, std::move(callback)));
EXPECT_EQ(GetWatchersCount(), 0);
}
TEST_F(BleV2MediumTest, StartScanningAdapterNoAdapterOnForge) {
EXPECT_CALL(bluetooth_adapter_, IsEnabled()).WillOnce(Return(true));
Uuid service_uuid = kTestServiceUuid;
api::ble_v2::BleMedium::ScanCallback callback;
EXPECT_EQ(false, blev2_medium_->StartScanning(
service_uuid, api::ble_v2::TxPowerLevel::kHigh, std::move(callback)));
EXPECT_EQ(GetWatchersCount(), 0);
}
TEST(BleV2Medium, DISABLED_StartAdvertising) {
BluetoothAdapter bluetoothAdapter;