Add windows impl for StartScanning

PiperOrigin-RevId: 534901836
This commit is contained in:
Hai Shang
2023-05-24 10:47:06 -07:00
committed by Copybara-Service
parent b80426a043
commit ecca9c1d19
6 changed files with 277 additions and 4 deletions
@@ -20,6 +20,7 @@
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/escaping.h"
#include "absl/strings/numbers.h"
@@ -27,6 +28,7 @@
#include "absl/strings/str_format.h"
#include "absl/synchronization/mutex.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/cancellation_flag.h"
#include "internal/platform/cancellation_flag_listener.h"
#include "internal/platform/implementation/ble_v2.h"
@@ -36,6 +38,8 @@
#include "internal/platform/implementation/windows/ble_v2_socket.h"
#include "internal/platform/implementation/windows/utils.h"
#include "internal/platform/logging.h"
#include "internal/platform/prng.h"
#include "internal/platform/uuid.h"
#include "winrt/Windows.Devices.Bluetooth.Advertisement.h"
#include "winrt/Windows.Devices.Bluetooth.h"
#include "winrt/Windows.Foundation.Collections.h"
@@ -98,6 +102,11 @@ std::string TxPowerLevelToName(TxPowerLevel tx_power_level) {
}
}
// Max times trying to generate unused session id.
static constexpr uint64_t kGenerateSessionIdRetryLimit = 3;
// Indicating failed to generate unused session id.
static constexpr uint64_t kFailedGenerateSessionId = 0;
} // namespace
BleV2Medium::BleV2Medium(api::BluetoothAdapter& adapter)
@@ -239,10 +248,113 @@ bool BleV2Medium::StartScanning(const Uuid& service_uuid,
std::unique_ptr<BleV2Medium::ScanningSession> BleV2Medium::StartScanning(
const Uuid& service_uuid, TxPowerLevel tx_power_level,
BleV2Medium::ScanningCallback callback) {
NEARBY_LOGS(INFO) << __func__ << ": Start scanning.";
NEARBY_LOGS(INFO) << __func__
<< ": service UUID: " << std::string(service_uuid)
<< ", TxPowerLevel: " << TxPowerLevelToName(tx_power_level);
if (!adapter_->IsEnabled()) {
NEARBY_LOGS(WARNING) << __func__
<< "BLE cannot start scanning because the "
"Bluetooth adapter is not enabled.";
return nullptr;
}
if (!is_watcher_started_) {
NEARBY_LOGS(WARNING) << __func__ << ": Starting BLE Scanning.";
try {
watcher_ = BluetoothLEAdvertisementWatcher();
watcher_token_ = watcher_.Stopped({this, &BleV2Medium::WatcherHandler});
advertisement_received_token_ =
watcher_.Received({this, &BleV2Medium::AdvertisementFoundHandler});
// TODO(hais): add real impl for windows StartAdvertising.
return std::make_unique<ScanningSession>(ScanningSession{});
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);
::winrt::Windows::Devices::Bluetooth::BluetoothSignalStrengthFilter
filter;
filter.SamplingInterval(TimeSpan(std::chrono::seconds(2)));
watcher_.SignalStrengthFilter(filter);
watcher_.Start();
is_watcher_started_ = true;
NEARBY_LOGS(INFO) << __func__ << ": BLE scanning started.";
} catch (std::exception exception) {
NEARBY_LOGS(ERROR) << __func__ << ": Exception to start BLE scanning: "
<< exception.what();
return nullptr;
} catch (const winrt::hresult_error& ex) {
NEARBY_LOGS(ERROR) << __func__
<< ": Exception to start BLE scanning: " << ex.code()
<< ": " << winrt::to_string(ex.message());
return nullptr;
}
} else {
NEARBY_LOGS(WARNING) << __func__ << ": BLE Scanning already started.";
}
uint64_t session_id = GenerateSessionId();
// Save session id, service id and callback for this scan session.
{
absl::MutexLock lock(&map_mutex_);
auto iter = service_uuid_to_session_map_.find(service_uuid);
if (iter == service_uuid_to_session_map_.end()) {
service_uuid_to_session_map_[service_uuid].insert(
{session_id, std::move(callback)});
} else {
iter->second.insert({session_id, std::move(callback)});
}
}
// Generate and return ScanningSession.
return std::make_unique<BleV2Medium::ScanningSession>(
BleV2Medium::ScanningSession{
.stop_scanning =
[this, session_id, service_uuid]() {
size_t num_erased_from_service_and_session_map = 0u;
{
absl::MutexLock lock(&map_mutex_);
auto iter = service_uuid_to_session_map_.find(service_uuid);
if (iter != service_uuid_to_session_map_.end()) {
num_erased_from_service_and_session_map =
iter->second.erase(session_id);
}
if (num_erased_from_service_and_session_map != 1u) {
return absl::NotFoundError(
"Can't find the provided internal session");
}
if (service_uuid_to_session_map_[service_uuid].empty()) {
service_uuid_to_session_map_.erase(service_uuid);
}
// Stop discovery if there's no more on-going scan sessions.
if (service_uuid_to_session_map_.empty()) {
try {
NEARBY_LOGS(INFO)
<< "No more scan sessions, stopping Ble scanning.";
watcher_.Stop();
is_watcher_started_ = false;
NEARBY_LOGS(INFO) << "Ble stoped scanning successfully.";
} catch (std::exception exception) {
NEARBY_LOGS(ERROR)
<< __func__ << ": Exception to stop BLE scanning: "
<< exception.what();
return absl::InternalError(
"Bad status stopping Ble scanning");
} catch (const winrt::hresult_error& ex) {
NEARBY_LOGS(ERROR)
<< __func__
<< ": Exception to stop BLE scanning: " << ex.code()
<< ": " << winrt::to_string(ex.message());
return absl::InternalError(
"Bad status stopping Ble scanning");
}
}
}
return absl::OkStatus();
},
});
}
std::unique_ptr<api::ble_v2::GattServer> BleV2Medium::StartGattServer(
@@ -843,6 +955,100 @@ void BleV2Medium::AdvertisementReceivedHandler(
}
}
void BleV2Medium::AdvertisementFoundHandler(
BluetoothLEAdvertisementWatcher watcher,
BluetoothLEAdvertisementReceivedEventArgs args) {
BluetoothLEAdvertisement advertisement = args.Advertisement();
std::vector<Uuid> service_uuid_list;
bool found_matching_service_uuid = false;
{
absl::MutexLock lock(&map_mutex_);
for (auto windows_service_uuid : advertisement.ServiceUuids()) {
auto nearby_service_uuid =
winrt_guid_to_nearby_uuid(windows_service_uuid);
service_uuid_list.push_back(nearby_service_uuid);
if (service_uuid_to_session_map_.find(nearby_service_uuid) !=
service_uuid_to_session_map_.end()) {
found_matching_service_uuid = true;
}
}
}
if (!found_matching_service_uuid) {
return;
}
// Construct BleAdvertisementData from the windows Advertisement.
api::ble_v2::BleAdvertisementData ble_advertisement_data;
for (BluetoothLEAdvertisementDataSection service_data :
advertisement.GetSectionsByType(0x16)) {
// Parse Advertisement Data for Section 0x16 (Service Data)
DataReader data_reader = DataReader::FromBuffer(service_data.Data());
// 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();
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)) {
std::string data;
uint8_t unconsumed_buffer_length = data_reader.UnconsumedBufferLength();
if (unconsumed_buffer_length > 27) {
NEARBY_LOGS(INFO) << "Skipping extended advertisement with service "
<< service_uuid.Get16BitAsString();
return;
}
for (int i = 0; i < unconsumed_buffer_length; i++) {
data.append(1, static_cast<unsigned char>(data_reader.ReadByte()));
}
ble_advertisement_data.service_data[service_uuid] = ByteArray{data};
}
}
}
if (ble_advertisement_data.service_data.empty()) {
NEARBY_LOGS(ERROR) << "Got matching Service UUID but found no "
"corresponding data, skipping";
return;
}
// Save the BleV2Peripheral.
std::string bluetooth_address =
uint64_to_mac_address_string(args.BluetoothAddress());
BleV2Peripheral* peripheral_ptr = nullptr;
{
absl::MutexLock lock(&map_mutex_);
if (address_to_peripheral_map_.find(bluetooth_address) ==
address_to_peripheral_map_.end()) {
NEARBY_LOGS(INFO) << "New BLE peripheral with address: "
<< bluetooth_address;
address_to_peripheral_map_[bluetooth_address] =
std::make_unique<BleV2Peripheral>();
} else {
NEARBY_LOGS(INFO) << "Already existing BLE peripheral with address: "
<< bluetooth_address;
}
address_to_peripheral_map_[bluetooth_address]->SetAddress(
bluetooth_address);
peripheral_ptr = address_to_peripheral_map_[bluetooth_address].get();
}
// Invokes callbacks that matches the UUID.
for (auto service_uuid : service_uuid_list) {
{
absl::MutexLock lock(&map_mutex_);
if (service_uuid_to_session_map_.find(service_uuid) !=
service_uuid_to_session_map_.end()) {
for (auto& id_session_pair :
service_uuid_to_session_map_[service_uuid]) {
id_session_pair.second.advertisement_found_cb(*peripheral_ptr,
ble_advertisement_data);
}
}
}
}
}
bool BleV2Medium::GetRemotePeripheral(const std::string& mac_address,
GetRemotePeripheralCallback callback) {
for (auto& item : peripherals_) {
@@ -872,5 +1078,18 @@ bool BleV2Medium::GetRemotePeripheral(api::ble_v2::BlePeripheral::UniqueId id,
return true;
}
uint64_t BleV2Medium::GenerateSessionId() {
absl::MutexLock lock(&map_mutex_);
for (int i = 0; i < kGenerateSessionIdRetryLimit; i++) {
uint64_t session_id = Prng().NextInt64();
if (session_id == kFailedGenerateSessionId) continue;
for (auto& element : service_uuid_to_session_map_) {
if (element.second.find(session_id) != element.second.end()) continue;
}
return session_id;
}
return kFailedGenerateSessionId;
}
} // namespace windows
} // namespace nearby
@@ -100,11 +100,19 @@ class BleV2Medium : public api::ble_v2::BleMedium {
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementReceivedEventArgs args);
void AdvertisementFoundHandler(
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher watcher,
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementReceivedEventArgs args);
void WatcherHandler(winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcher watcher,
winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementWatcherStoppedEventArgs args);
uint64_t GenerateSessionId();
BluetoothAdapter* adapter_;
Uuid service_uuid_;
api::ble_v2::TxPowerLevel tx_power_level_;
@@ -116,6 +124,14 @@ class BleV2Medium : public api::ble_v2::BleMedium {
absl::flat_hash_map<std::string, std::unique_ptr<BleV2Peripheral>>
peripheral_map_ ABSL_GUARDED_BY(peripheral_map_mutex_);
absl::Mutex map_mutex_;
// std::map<std::string, std::unique_ptr<BleV2Peripheral>>
absl::flat_hash_map<std::string, std::unique_ptr<BleV2Peripheral>>
address_to_peripheral_map_ ABSL_GUARDED_BY(map_mutex_);
// std::map<Uuid, std::map<uint64_t, ScanningCallback>>
absl::flat_hash_map<Uuid, absl::flat_hash_map<uint64_t, ScanningCallback>>
service_uuid_to_session_map_ ABSL_GUARDED_BY(map_mutex_);
// WinRT objects
::winrt::Windows::Devices::Bluetooth::Advertisement::
BluetoothLEAdvertisementPublisher publisher_ = nullptr;
@@ -15,6 +15,7 @@
#include "internal/platform/implementation/windows/ble_v2.h"
#include <string>
#include <utility>
#include "gtest/gtest.h"
#include "absl/synchronization/notification.h"
@@ -90,6 +91,29 @@ TEST(BleV2Medium, DISABLED_StopScanning) {
EXPECT_TRUE(blev2_medium.StopScanning());
}
TEST(BleV2Medium, DISABLED_StartThenStopScanning) {
bool scan_response_received = false;
absl::Notification scan_response_notification;
BluetoothAdapter bluetoothAdapter;
BleV2Medium blev2_medium(bluetoothAdapter);
Uuid service_uuid;
api::ble_v2::BleMedium::ScanningCallback callback;
callback.advertisement_found_cb =
[&scan_response_received, &scan_response_notification](
api::ble_v2::BlePeripheral& peripheral,
const api::ble_v2::BleAdvertisementData& advertisement_data) {
scan_response_received = true;
scan_response_notification.Notify();
};
auto scanning_session = blev2_medium.StartScanning(
service_uuid, api::ble_v2::TxPowerLevel::kHigh, std::move(callback));
EXPECT_NE(scanning_session, nullptr);
EXPECT_TRUE(scanning_session->stop_scanning().ok());
}
} // namespace
} // namespace windows
} // namespace nearby