mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
Add windows impl for StartScanning
PiperOrigin-RevId: 534901836
This commit is contained in:
committed by
Copybara-Service
parent
b80426a043
commit
ecca9c1d19
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user