From 080e964a4c9e67b8022591ff06631075847f215b Mon Sep 17 00:00:00 2001 From: edwinwu Date: Thu, 12 May 2022 20:27:16 -0700 Subject: [PATCH] [BLE Refactor] Implements GattClient for sender's device to fetch Gatt advertisement. PiperOrigin-RevId: 448400837 --- connections/implementation/mediums/ble_v2.cc | 95 +++++-- connections/implementation/mediums/ble_v2.h | 2 - .../implementation/mediums/ble_v2_test.cc | 268 +++++++++++++----- internal/platform/BUILD | 1 + internal/platform/ble_v2.cc | 35 ++- internal/platform/ble_v2.h | 97 +++++-- internal/platform/ble_v2_test.cc | 264 +++++++++++++---- internal/platform/implementation/ble_v2.h | 91 ++---- internal/platform/implementation/g3/ble_v2.cc | 116 +++++++- internal/platform/implementation/g3/ble_v2.h | 50 ++-- .../platform/implementation/windows/ble_v2.cc | 6 +- .../platform/implementation/windows/ble_v2.h | 5 +- internal/platform/medium_environment.cc | 76 ++++- internal/platform/medium_environment.h | 28 +- 14 files changed, 843 insertions(+), 291 deletions(-) diff --git a/connections/implementation/mediums/ble_v2.cc b/connections/implementation/mediums/ble_v2.cc index f028adcf..dc14f53d 100644 --- a/connections/implementation/mediums/ble_v2.cc +++ b/connections/implementation/mediums/ble_v2.cc @@ -77,6 +77,8 @@ bool BleV2::IsAvailable() const { } // TODO(edwinwu): Break down the function. +// TODO(b/229927044): Use bool: is_fast_advertisement, not +// fast_advertisement_service_uuid. bool BleV2::StartAdvertising( const std::string& service_id, const ByteArray& advertisement_bytes, PowerLevel power_level, @@ -273,6 +275,7 @@ bool BleV2::IsAdvertising(const std::string& service_id) const { return IsAdvertisingLocked(service_id); } +// TODO(b/229927044): Remove param: fast_advertisement_service_uuid. bool BleV2::StartScanning(const std::string& service_id, PowerLevel power_level, DiscoveredPeripheralCallback callback, const std::string& fast_advertisement_service_uuid) { @@ -314,13 +317,27 @@ bool BleV2::StartScanning(const std::string& service_id, PowerLevel power_level, return true; } + // Check if scan has been activated, if yes, no need to notify client + // to scan again. + if (!scanned_service_ids_.empty()) { + scanned_service_ids_.insert(service_id); + NEARBY_LOGS(INFO) << "Turned on BLE scanning with service id=" << service_id + << " without start client scanning"; + return true; + } + scanned_service_ids_.insert(service_id); // TODO(b/213835576): We should re-start scanning once the power level is // changed. - std::vector service_uuids{ - std::string(mediums::bleutils::kCopresenceServiceUuid)}; + std::vector scanning_service_uuids; + if (!fast_advertisement_service_uuid.empty()) { + scanning_service_uuids.push_back(fast_advertisement_service_uuid); + } else { + scanning_service_uuids.push_back( + std::string(mediums::bleutils::kCopresenceServiceUuid)); + } if (!medium_.StartScanning( - service_uuids, PowerLevelToPowerMode(power_level), + scanning_service_uuids, PowerLevelToPowerMode(power_level), { .advertisement_found_cb = [this](BleV2Peripheral peripheral, @@ -428,18 +445,8 @@ bool BleV2::StartAdvertisementGattServerLocked( return false; } - std::unique_ptr gatt_server = medium_.StartGattServer({ - .characteristic_subscription_cb = - [](const ServerGattConnection& connection, - const GattCharacteristic& characteristic) { - // TODO(b/213835576): Impl or remove. - }, - .characteristic_unsubscription_cb = - [](const ServerGattConnection& connection, - const GattCharacteristic& characteristic) { - // TODO(b/213835576): Impl or remove. - }, - }); + std::unique_ptr gatt_server = + medium_.StartGattServer(/*ServerGattConnectionCallback=*/{}); if (!gatt_server || !gatt_server->IsValid()) { NEARBY_LOGS(INFO) << "Unable to start an advertisement GATT server."; return false; @@ -508,7 +515,63 @@ void BleV2::ProcessFetchGattAdvertisementsRequest( return; } - // TODO(edwinwu): Attempt to connect and read some GATT characteristics. + // Connect to a GATT server, reads advertisement data, and then disconnect + // from the GATT server. + bool read_success = true; + std::unique_ptr gatt_client = medium_.ConnectToGattServer( + std::move(peripheral), PowerLevelToPowerMode(PowerLevel::kHighPower), + /*ClientGattConnectionCallback=*/{}); + if (!gatt_client || !gatt_client->IsValid()) { + advertisement_read_result.RecordLastReadStatus(false); + return; + } + + // Always use kCopresenceServiceUuid for service uuid. + std::string service_uuid = + std::string(mediums::bleutils::kCopresenceServiceUuid); + if (!gatt_client->DiscoverService(service_uuid)) { + NEARBY_LOGS(WARNING) << "GATT client can't discover service."; + advertisement_read_result.RecordLastReadStatus(false); + return; + } + + // Read all advertisements from all slots that we haven't read from yet. + for (int slot = 0; slot < num_slots; ++slot) { + // Make sure we haven't already read this advertisement before. + if (advertisement_read_result.HasAdvertisement(slot)) { + continue; + } + + // Make sure the characteristic even exists for this slot number. If + // the characteristic doesn't exist, we shouldn't count the fetch as a + // failure because there's nothing we could've done about a + // non-existed characteristic. + auto gatt_characteristic = gatt_client->GetCharacteristic( + std::string(mediums::bleutils::kCopresenceServiceUuid), + mediums::bleutils::GenerateAdvertisementUuid(slot)); + if (!gatt_characteristic.has_value()) { + continue; + } + + // Read advertisement data from the characteristic associated with this + // slot. + auto characteristic_byte = + gatt_client->ReadCharacteristic(gatt_characteristic.value()); + if (characteristic_byte.has_value()) { + advertisement_read_result.AddAdvertisement(slot, *characteristic_byte); + NEARBY_LOGS(VERBOSE) << "Successfully read advertisement at slot=" + << slot; + } else { + NEARBY_LOGS(WARNING) << "Can't read advertisement for slot=" << slot; + read_success = false; + } + // Whether or not we succeeded with this slot, we should try reading the + // other slots to get as many advertisements as possible before + // returning a success or failure. + } + gatt_client->Disconnect(); + + advertisement_read_result.RecordLastReadStatus(read_success); } bool BleV2::StopAdvertisementGattServerLocked() { diff --git a/connections/implementation/mediums/ble_v2.h b/connections/implementation/mediums/ble_v2.h index 1043810d..679d1c07 100644 --- a/connections/implementation/mediums/ble_v2.h +++ b/connections/implementation/mediums/ble_v2.h @@ -42,8 +42,6 @@ namespace connections { // (BLE) medium. class BleV2 final { public: - using ServerGattConnectionCallback = - BleV2Medium::ServerGattConnectionCallback; using DiscoveredPeripheralCallback = mediums::DiscoveredPeripheralCallback; static constexpr absl::Duration kPeripheralLostTimeout = absl::Seconds(3); diff --git a/connections/implementation/mediums/ble_v2_test.cc b/connections/implementation/mediums/ble_v2_test.cc index cc6d74ce..a2a7a367 100644 --- a/connections/implementation/mediums/ble_v2_test.cc +++ b/connections/implementation/mediums/ble_v2_test.cc @@ -28,11 +28,11 @@ namespace connections { namespace { constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000); -constexpr absl::string_view kServiceIDA{ - "com.google.location.nearby.apps.test.a"}; -constexpr absl::string_view kServiceIDB{ - "com.google.location.nearby.apps.test.b"}; -constexpr absl::string_view kAdvertisementString{"\x0a\x0b\x0c\x0d"}; +constexpr absl::string_view kServiceIDA = + "com.google.location.nearby.apps.test.a"; +constexpr absl::string_view kServiceIDB = + "com.google.location.nearby.apps.test.b"; +constexpr absl::string_view kAdvertisementString = "\x0a\x0b\x0c\x0d"; constexpr absl::string_view kFastAdvertisementServiceUuid = "0000FE2C-0000-1000-8000-00805F9B34FB"; @@ -47,8 +47,8 @@ TEST_F(BleV2Test, CanConstructValidObject) { env_.Start(); BluetoothRadio radio_a; BluetoothRadio radio_b; - BleV2 ble_a{radio_a}; - BleV2 ble_b{radio_b}; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); EXPECT_TRUE(ble_a.IsMediumValid()); EXPECT_TRUE(ble_a.IsAvailable()); @@ -58,58 +58,15 @@ TEST_F(BleV2Test, CanConstructValidObject) { env_.Stop(); } -TEST_F(BleV2Test, CanStartAdvertising) { - env_.Start(); - BluetoothRadio radio; - BleV2 ble{radio}; - radio.Enable(); - ByteArray advertisement_bytes{std::string(kAdvertisementString)}; - - EXPECT_TRUE(ble.StartAdvertising(std::string(kServiceIDA), - advertisement_bytes, PowerLevel::kHighPower, - /*fast_advertisement_service_uuid=*/"")); - // Can't advertise twice for the same service_id. - EXPECT_FALSE(ble.StartAdvertising(std::string(kServiceIDA), - advertisement_bytes, PowerLevel::kHighPower, - /*fast_advertisement_service_uuid=*/"")); - EXPECT_TRUE(ble.StopAdvertising(std::string(kServiceIDA))); - env_.Stop(); -} - -TEST_F(BleV2Test, CanStartScanning) { - env_.Start(); - BluetoothRadio radio; - BleV2 ble{radio}; - radio.Enable(); - - EXPECT_TRUE(ble.StartScanning( - std::string(kServiceIDA), PowerLevel::kHighPower, - mediums::DiscoveredPeripheralCallback{ - .peripheral_discovered_cb = - [](BleV2Peripheral peripheral, const std::string& service_id, - const ByteArray& advertisement_bytes, - bool fast_advertisement) { - // nothing to do for now - }, - .peripheral_lost_cb = - [](BleV2Peripheral peripheral, const std::string& service_id) { - // nothing to do for now - }, - }, - /*fast_advertisement_service_uuid=*/"")); - EXPECT_TRUE(ble.StopScanning(std::string(kServiceIDA))); - env_.Stop(); -} - TEST_F(BleV2Test, CanStartFastAdvertising) { env_.Start(); BluetoothRadio radio_a; BluetoothRadio radio_b; - BleV2 ble_a{radio_a}; - BleV2 ble_b{radio_b}; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); radio_a.Enable(); radio_b.Enable(); - ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + ByteArray advertisement_bytes((std::string(kAdvertisementString))); CountDownLatch found_latch(1); ble_b.StartScanning( @@ -123,10 +80,6 @@ TEST_F(BleV2Test, CanStartFastAdvertising) { EXPECT_TRUE(fast_advertisement); found_latch.CountDown(); }, - .peripheral_lost_cb = - [](BleV2Peripheral peripheral, const std::string& service_id) { - // nothing to do for now - }, }, std::string(kFastAdvertisementServiceUuid)); @@ -143,11 +96,11 @@ TEST_F(BleV2Test, CanStartFastScanning) { env_.Start(); BluetoothRadio radio_a; BluetoothRadio radio_b; - BleV2 ble_a{radio_a}; - BleV2 ble_b{radio_b}; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); radio_a.Enable(); radio_b.Enable(); - ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + ByteArray advertisement_bytes((std::string(kAdvertisementString))); CountDownLatch found_latch(1); ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes, @@ -165,10 +118,6 @@ TEST_F(BleV2Test, CanStartFastScanning) { EXPECT_TRUE(fast_advertisement); found_latch.CountDown(); }, - .peripheral_lost_cb = - [](BleV2Peripheral peripheral, const std::string& service_id) { - // nothing to do for now - }, }, std::string(kFastAdvertisementServiceUuid))); @@ -178,10 +127,79 @@ TEST_F(BleV2Test, CanStartFastScanning) { env_.Stop(); } +TEST_F(BleV2Test, CanStartAdvertising) { + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); + radio_a.Enable(); + radio_b.Enable(); + ByteArray advertisement_bytes((std::string(kAdvertisementString))); + CountDownLatch found_latch(1); + + ble_b.StartScanning( + std::string(kServiceIDA), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_FALSE(fast_advertisement); + found_latch.CountDown(); + }, + }, + /*fast_advertisement_service_uuid=*/""); + + EXPECT_TRUE(ble_a.StartAdvertising( + std::string(kServiceIDA), advertisement_bytes, PowerLevel::kHighPower, + /*fast_advertisement_service_uuid=*/"")); + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_a.StopAdvertising(std::string(kServiceIDA))); + ble_b.StopScanning(std::string(kServiceIDA)); + env_.Stop(); +} + +TEST_F(BleV2Test, CanStartScanning) { + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); + radio_a.Enable(); + radio_b.Enable(); + ByteArray advertisement_bytes((std::string(kAdvertisementString))); + CountDownLatch found_latch(1); + + ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes, + PowerLevel::kHighPower, + /*fast_advertisement_service_uuid=*/""); + + EXPECT_TRUE(ble_a.StartScanning( + std::string(kServiceIDA), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_FALSE(fast_advertisement); + found_latch.CountDown(); + }, + }, + /*fast_advertisement_service_uuid=*/"")); + + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + ble_b.StopAdvertising(std::string(kServiceIDA)); + EXPECT_TRUE(ble_a.StopScanning(std::string(kServiceIDA))); + env_.Stop(); +} + TEST_F(BleV2Test, CanStartStopMultipleScanningWithDifferentServiceIds) { env_.Start(); BluetoothRadio radio; - BleV2 ble{radio}; + BleV2 ble(radio); radio.Enable(); EXPECT_TRUE(ble.StartScanning(std::string(kServiceIDA), @@ -207,12 +225,12 @@ TEST_F(BleV2Test, DestructWorksForStartAdvertisingAndScanningWithoutStop) { env_.Start(); BluetoothRadio radio_a; BluetoothRadio radio_b; - BleV2 ble_a{radio_a}; - BleV2 ble_b{radio_b}; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); radio_a.Enable(); radio_b.Enable(); - ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + ByteArray advertisement_bytes((std::string(kAdvertisementString))); // Device A starts advertising with service IDA and IDB. EXPECT_TRUE(ble_a.StartAdvertising( @@ -234,15 +252,15 @@ TEST_F(BleV2Test, DestructWorksForStartAdvertisingAndScanningWithoutStop) { env_.Stop(); } -TEST_F(BleV2Test, StartScanningDiscoverAndLostPeripheral) { +TEST_F(BleV2Test, StartFastScanningDiscoverAndLostPeripheral) { env_.Start(); BluetoothRadio radio_a; BluetoothRadio radio_b; - BleV2 ble_a{radio_a}; - BleV2 ble_b{radio_b}; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); radio_a.Enable(); radio_b.Enable(); - ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + ByteArray advertisement_bytes((std::string(kAdvertisementString))); CountDownLatch found_latch(1); CountDownLatch lost_latch(1); @@ -273,7 +291,7 @@ TEST_F(BleV2Test, StartScanningDiscoverAndLostPeripheral) { ble_b.StopAdvertising(std::string(kServiceIDA)); - // Wait for a while (2 times delay) to let the alaram occur twice and + // Wait for a while (2 times delay) to let the alarm occur twice and // `ProcessLostGattAdvertisements` twice to lost periperal. SystemClock::Sleep(BleV2::kPeripheralLostTimeout * 2); @@ -283,15 +301,16 @@ TEST_F(BleV2Test, StartScanningDiscoverAndLostPeripheral) { env_.Stop(); } -TEST_F(BleV2Test, StartScanningDiscoverButNoPeripheralLostAfterStopScanning) { +TEST_F(BleV2Test, + StartFastScanningDiscoverButNoPeripheralLostAfterStopScanning) { env_.Start(); BluetoothRadio radio_a; BluetoothRadio radio_b; - BleV2 ble_a{radio_a}; - BleV2 ble_b{radio_b}; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); radio_a.Enable(); radio_b.Enable(); - ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + ByteArray advertisement_bytes((std::string(kAdvertisementString))); CountDownLatch found_latch(1); CountDownLatch lost_latch(1); @@ -330,6 +349,101 @@ TEST_F(BleV2Test, StartScanningDiscoverButNoPeripheralLostAfterStopScanning) { env_.Stop(); } +TEST_F(BleV2Test, StartScanningDiscoverAndLostPeripheral) { + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); + radio_a.Enable(); + radio_b.Enable(); + ByteArray advertisement_bytes((std::string(kAdvertisementString))); + CountDownLatch found_latch(1); + CountDownLatch lost_latch(1); + + ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes, + PowerLevel::kHighPower, + /*fast_advertisement_service_uuid=*/""); + + ble_a.StartScanning( + std::string(kServiceIDA), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_FALSE(fast_advertisement); + found_latch.CountDown(); + }, + .peripheral_lost_cb = + [&lost_latch](BleV2Peripheral peripheral, + const std::string& service_id) { + lost_latch.CountDown(); + }, + }, + /*fast_advertisement_service_uuid=*/""); + + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + + ble_b.StopAdvertising(std::string(kServiceIDA)); + + // Wait for a while (2 times delay) to let the alarm occur twice and + // `ProcessLostGattAdvertisements` twice to lost periperal. + SystemClock::Sleep(BleV2::kPeripheralLostTimeout * 2); + + EXPECT_TRUE(lost_latch.Await(kWaitDuration).result()); + + ble_a.StopScanning(std::string(kServiceIDA)); + env_.Stop(); +} + +TEST_F(BleV2Test, StartScanningDiscoverButNoPeripheralLostAfterStopScanning) { + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + BleV2 ble_a(radio_a); + BleV2 ble_b(radio_b); + radio_a.Enable(); + radio_b.Enable(); + ByteArray advertisement_bytes((std::string(kAdvertisementString))); + CountDownLatch found_latch(1); + CountDownLatch lost_latch(1); + + ble_b.StartAdvertising(std::string(kServiceIDA), advertisement_bytes, + PowerLevel::kHighPower, ""); + + ble_a.StartScanning( + std::string(kServiceIDA), PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BleV2Peripheral peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + EXPECT_FALSE(fast_advertisement); + found_latch.CountDown(); + }, + .peripheral_lost_cb = + [&lost_latch](BleV2Peripheral peripheral, + const std::string& service_id) { + lost_latch.CountDown(); + }, + }, + /*fast_advertisement_service_uuid=*/""); + + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + + ble_b.StopAdvertising(std::string(kServiceIDA)); + ble_a.StopScanning(std::string(kServiceIDA)); + + // Don't receive lost peripheral callback because we have stopped scanning and + // cancelled the alarm. + EXPECT_FALSE(lost_latch.Await(kWaitDuration).result()); + + env_.Stop(); +} + } // namespace } // namespace connections } // namespace nearby diff --git a/internal/platform/BUILD b/internal/platform/BUILD index 10d890eb..3831c1bb 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -340,6 +340,7 @@ cc_library( "//internal/platform/implementation:platform", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:optional", ], ) diff --git a/internal/platform/ble_v2.cc b/internal/platform/ble_v2.cc index 45ea037d..e60414ca 100644 --- a/internal/platform/ble_v2.cc +++ b/internal/platform/ble_v2.cc @@ -15,6 +15,7 @@ #include "internal/platform/ble_v2.h" #include +#include #include #include "internal/platform/bluetooth_adapter.h" @@ -102,25 +103,41 @@ std::unique_ptr BleV2Medium::StartGattServer( std::unique_ptr api_gatt_server = impl_->StartGattServer({ .characteristic_subscription_cb = - [this](api::ble_v2::ServerGattConnection& connection, - const GattCharacteristic& characteristic) { + [this](const GattCharacteristic& characteristic) { MutexLock lock(&mutex_); - ServerGattConnection server_gatt_connection(&connection); server_gatt_connection_callback_.characteristic_subscription_cb( - server_gatt_connection, characteristic); + characteristic); }, .characteristic_unsubscription_cb = - [this](api::ble_v2::ServerGattConnection& connection, - const GattCharacteristic& characteristic) { + [this](const GattCharacteristic& characteristic) { MutexLock lock(&mutex_); - ServerGattConnection server_gatt_connection(&connection); server_gatt_connection_callback_ - .characteristic_unsubscription_cb(server_gatt_connection, - characteristic); + .characteristic_unsubscription_cb(characteristic); }, }); return std::make_unique(std::move(api_gatt_server)); } +std::unique_ptr BleV2Medium::ConnectToGattServer( + BleV2Peripheral peripheral, PowerMode power_mode, + ClientGattConnectionCallback callback) { + { + MutexLock lock(&mutex_); + client_gatt_connection_callback_ = std::move(callback); + } + + std::unique_ptr api_gatt_client = + impl_->ConnectToGattServer( + peripheral.GetImpl(), power_mode, + { + .disconnected_cb = + [this]() { + MutexLock lock(&mutex_); + client_gatt_connection_callback_.disconnected_cb(); + }, + }); + return std::make_unique(std::move(api_gatt_client)); +} + } // namespace nearby } // namespace location diff --git a/internal/platform/ble_v2.h b/internal/platform/ble_v2.h index 3ad4b3ec..25ac919b 100644 --- a/internal/platform/ble_v2.h +++ b/internal/platform/ble_v2.h @@ -16,9 +16,11 @@ #define PLATFORM_PUBLIC_BLE_V2_H_ #include +#include #include #include +#include "absl/types/optional.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/byte_array.h" #include "internal/platform/implementation/ble_v2.h" @@ -27,38 +29,18 @@ namespace location { namespace nearby { -// Opaque wrapper over a ServerGattConnection. -class ServerGattConnection final { - public: - ServerGattConnection() = default; - explicit ServerGattConnection( - api::ble_v2::ServerGattConnection* server_gatt_connection) - : impl_(server_gatt_connection) {} - - bool SendCharacteristic(const api::ble_v2::GattCharacteristic& characteristic, - const ByteArray& value) { - return impl_->SendCharacteristic(characteristic, value); - } - - api::ble_v2::ServerGattConnection* GetImpl() { return impl_; } - - bool IsValid() const { return impl_ != nullptr; } - - private: - api::ble_v2::ServerGattConnection* impl_ = nullptr; -}; - // Opaque wrapper over a GattServer. // Move only, disallow copy. +// +// Note that some of the methods return absl::optional instead +// of std::optional, because iOS platform is still in C++14. class GattServer final { public: - GattServer() = default; explicit GattServer(std::unique_ptr gatt_server) : impl_(std::move(gatt_server)) {} - GattServer(GattServer&&) = default; - GattServer& operator=(GattServer&&) = default; ~GattServer() { Stop(); } + // NOLINTNEXTLINE(google3-legacy-absl-backports) absl::optional CreateCharacteristic( const std::string& service_uuid, const std::string& characteristic_uuid, const std::vector& @@ -89,6 +71,47 @@ class GattServer final { std::unique_ptr impl_; }; +// Opaque wrapper for a GattClient. +// +// Note that some of the methods return absl::optional instead +// of std::optional, because iOS platform is still in C++14. +class GattClient final { + public: + explicit GattClient( + std::unique_ptr client_gatt_connection) + : impl_(std::move(client_gatt_connection)) {} + + bool DiscoverService(const std::string& service_uuid) { + return impl_->DiscoverService(service_uuid); + } + + // TODO(edwinwu): Change std::string to Uuid. + // NOLINTNEXTLINE(google3-legacy-absl-backports) + absl::optional GetCharacteristic( + const std::string& service_uuid, const std::string& characteristic_uuid) { + return impl_->GetCharacteristic(service_uuid, characteristic_uuid); + } + + // NOLINTNEXTLINE(google3-legacy-absl-backports) + absl::optional ReadCharacteristic( + api::ble_v2::GattCharacteristic& characteristic) { + return impl_->ReadCharacteristic(characteristic); + } + + void Disconnect() { impl_->Disconnect(); } + + // Returns true if a client_gatt_connection is usable. If this method returns + // false, it is not safe to call any other method. + bool IsValid() const { return impl_ != nullptr; } + + // Returns reference to platform implementation. + // This is used to communicate with platform code, and for debugging purposes. + api::ble_v2::GattClient* GetImpl() { return impl_.get(); } + + private: + std::unique_ptr impl_; +}; + // Container of operations that can be performed over the BLE medium. class BleV2Medium final { public: @@ -107,16 +130,19 @@ class BleV2Medium final { advertisement_found_cb = location::nearby::DefaultCallback< BleV2Peripheral, const api::ble_v2::BleAdvertisementData&>(); }; - struct ServerGattConnectionCallback { - std::function + std::function characteristic_subscription_cb = location::nearby::DefaultCallback< - ServerGattConnection&, const api::ble_v2::GattCharacteristic&>(); - std::function + const api::ble_v2::GattCharacteristic&>(); + std::function characteristic_unsubscription_cb = location::nearby::DefaultCallback< - ServerGattConnection&, const api::ble_v2::GattCharacteristic&>(); + const api::ble_v2::GattCharacteristic&>(); + }; + // TODO(b/231318879): Remove this wrapper callback and use impl callback if + // there is only disconnect function here in the end. + struct ClientGattConnectionCallback { + std::function disconnected_cb = + location::nearby::DefaultCallback<>(); }; explicit BleV2Medium(BluetoothAdapter& adapter) @@ -136,9 +162,16 @@ class BleV2Medium final { api::ble_v2::PowerMode power_mode, ScanCallback callback); bool StopScanning(); + // Starts Gatt Server for waiting to client connection. std::unique_ptr StartGattServer( ServerGattConnectionCallback callback); + // Returns a new GattClient connection to a gatt server. + // There is only one instance of GattServer can run at a time. + std::unique_ptr ConnectToGattServer( + BleV2Peripheral peripheral, api::ble_v2::PowerMode power_mode, + ClientGattConnectionCallback callback); + bool IsValid() const { return impl_ != nullptr; } api::ble_v2::BleMedium* GetImpl() const { return impl_.get(); } @@ -149,6 +182,8 @@ class BleV2Medium final { BluetoothAdapter& adapter_; ServerGattConnectionCallback server_gatt_connection_callback_ ABSL_GUARDED_BY(mutex_); + ClientGattConnectionCallback client_gatt_connection_callback_ + ABSL_GUARDED_BY(mutex_); absl::flat_hash_set peripherals_ ABSL_GUARDED_BY(mutex_); ScanCallback scan_callback_ ABSL_GUARDED_BY(mutex_); diff --git a/internal/platform/ble_v2_test.cc b/internal/platform/ble_v2_test.cc index 7743e7cb..998ea813 100644 --- a/internal/platform/ble_v2_test.cc +++ b/internal/platform/ble_v2_test.cc @@ -17,7 +17,11 @@ #include #include +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "internal/platform/bluetooth_adapter.h" +#include "internal/platform/count_down_latch.h" #include "internal/platform/medium_environment.h" namespace location { @@ -27,12 +31,27 @@ namespace { using ::location::nearby::api::ble_v2::BleAdvertisementData; using ::location::nearby::api::ble_v2::GattCharacteristic; using ::location::nearby::api::ble_v2::PowerMode; +using ::testing::Optional; -constexpr absl::string_view kAdvertisementString{"\x0a\x0b\x0c\x0d"}; -constexpr absl::string_view kCopresenceServiceUuid{"F3FE"}; -constexpr absl::string_view kFastAdvertisementServiceUuid{"FAST"}; +constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000); +constexpr absl::string_view kAdvertisementString = "\x0a\x0b\x0c\x0d"; +constexpr absl::string_view kCopresenceServiceUuid = "F3FE"; +constexpr absl::string_view kFastAdvertisementServiceUuid = "FE2C"; constexpr PowerMode kPowerMode(PowerMode::kHigh); +// A stub BlePeripheral implementation. +class BlePeripheralStub : public api::ble_v2::BlePeripheral { + public: + explicit BlePeripheralStub(absl::string_view mac_address) { + mac_address_ = mac_address; + } + + std::string GetAddress() const override { return mac_address_; } + + private: + std::string mac_address_; +}; + class BleV2MediumTest : public testing::Test { protected: BleV2MediumTest() { env_.Stop(); } @@ -42,10 +61,10 @@ class BleV2MediumTest : public testing::Test { TEST_F(BleV2MediumTest, ConstructorDestructorWorks) { env_.Start(); - BluetoothAdapter adapter_a_; - BluetoothAdapter adapter_b_; - BleV2Medium ble_a{adapter_a_}; - BleV2Medium ble_b{adapter_b_}; + BluetoothAdapter adapter_a; + BluetoothAdapter adapter_b; + BleV2Medium ble_a(adapter_a); + BleV2Medium ble_b(adapter_b); // Make sure we can create functional mediums. ASSERT_TRUE(ble_a.IsValid()); @@ -56,75 +75,162 @@ TEST_F(BleV2MediumTest, ConstructorDestructorWorks) { env_.Stop(); } -TEST_F(BleV2MediumTest, CanStartFastAdvertising) { +TEST_F(BleV2MediumTest, CanStartFastScanningAndFastAdvertising) { env_.Start(); - BluetoothAdapter adapter_; - BleV2Medium ble{adapter_}; - ByteArray advertisement_bytes{std::string(kAdvertisementString)}; - std::string fast_advertisement_service_uuid(kFastAdvertisementServiceUuid); + BluetoothAdapter adapter_a; + BluetoothAdapter adapter_b; + BleV2Medium ble_a(adapter_a); + BleV2Medium ble_b(adapter_b); + CountDownLatch found_latch(1); - BleAdvertisementData advertising_data; - advertising_data.is_connectable = true; - advertising_data.tx_power_level = - BleAdvertisementData::kUnspecifiedTxPowerLevel; - advertising_data.service_uuids.insert(fast_advertisement_service_uuid); + EXPECT_TRUE(ble_a.StartScanning( + {std::string(kFastAdvertisementServiceUuid)}, kPowerMode, + { + .advertisement_found_cb = + [&found_latch](BleV2Peripheral peripheral, + const BleAdvertisementData& advertisement_data) { + found_latch.CountDown(); + }, + })); // Assemble fast advertising and scan response data. + BleAdvertisementData advertising_data; + advertising_data.service_uuids.insert( + std::string(kFastAdvertisementServiceUuid)); BleAdvertisementData scan_response_data; - scan_response_data.is_connectable = true; - scan_response_data.tx_power_level = - BleAdvertisementData::kUnspecifiedTxPowerLevel; scan_response_data.service_data.insert( - {fast_advertisement_service_uuid, advertisement_bytes}); + {std::string(kFastAdvertisementServiceUuid), + ByteArray(std::string(kAdvertisementString))}); EXPECT_TRUE( - ble.StartAdvertising(advertising_data, scan_response_data, kPowerMode)); - - EXPECT_TRUE(ble.StopAdvertising()); + ble_b.StartAdvertising(advertising_data, scan_response_data, kPowerMode)); + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_a.StopScanning()); + EXPECT_TRUE(ble_b.StopAdvertising()); env_.Stop(); } -TEST_F(BleV2MediumTest, CanStartAdvertising) { +TEST_F(BleV2MediumTest, CanStartScanningAndAdvertising) { env_.Start(); - BluetoothAdapter adapter_; - BleV2Medium ble{adapter_}; - ByteArray advertisement_header_bytes{std::string(kAdvertisementString)}; - std::string fast_advertisement_service_uuid(kFastAdvertisementServiceUuid); + BluetoothAdapter adapter_a; + BluetoothAdapter adapter_b; + BleV2Medium ble_a(adapter_a); + BleV2Medium ble_b(adapter_b); + CountDownLatch found_latch(1); + + EXPECT_TRUE(ble_a.StartScanning( + {std::string(kCopresenceServiceUuid)}, kPowerMode, + { + .advertisement_found_cb = + [&found_latch](BleV2Peripheral peripheral, + const BleAdvertisementData& advertisement_data) { + found_latch.CountDown(); + }, + })); // Assemble regular advertising and scan response data. - BleAdvertisementData advertising_data; - advertising_data.is_connectable = true; - advertising_data.tx_power_level = - BleAdvertisementData::kUnspecifiedTxPowerLevel; - + BleAdvertisementData advertising_data = {}; BleAdvertisementData scan_response_data; - scan_response_data.is_connectable = true; - scan_response_data.tx_power_level = - BleAdvertisementData::kUnspecifiedTxPowerLevel; - scan_response_data.service_uuids.insert(fast_advertisement_service_uuid); + scan_response_data.service_uuids.insert(std::string(kCopresenceServiceUuid)); scan_response_data.service_data.insert( - {fast_advertisement_service_uuid, advertisement_header_bytes}); + {std::string(kCopresenceServiceUuid), + ByteArray(std::string(kAdvertisementString))}); EXPECT_TRUE( - ble.StartAdvertising(advertising_data, scan_response_data, kPowerMode)); + ble_b.StartAdvertising(advertising_data, scan_response_data, kPowerMode)); + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_a.StopScanning()); + EXPECT_TRUE(ble_b.StopAdvertising()); + env_.Stop(); +} - EXPECT_TRUE(ble.StopAdvertising()); +TEST_F(BleV2MediumTest, + CanStartFastAdvertisingButRegularScanningFailToFoundAdvertisement) { + env_.Start(); + BluetoothAdapter adapter_a; + BluetoothAdapter adapter_b; + BleV2Medium ble_a(adapter_a); + BleV2Medium ble_b(adapter_b); + CountDownLatch found_latch(1); + + EXPECT_TRUE(ble_a.StartScanning( + {std::string(kCopresenceServiceUuid)}, kPowerMode, + { + .advertisement_found_cb = + [&found_latch](BleV2Peripheral peripheral, + const BleAdvertisementData& advertisement_data) { + found_latch.CountDown(); + }, + })); + + // Assemble fast advertising and scan response data. + BleAdvertisementData advertising_data; + advertising_data.service_uuids.insert( + std::string(kFastAdvertisementServiceUuid)); + BleAdvertisementData scan_response_data; + scan_response_data.service_data.insert( + {std::string(kFastAdvertisementServiceUuid), + ByteArray(std::string(kAdvertisementString))}); + + EXPECT_TRUE( + ble_b.StartAdvertising(advertising_data, scan_response_data, kPowerMode)); + // Fail to found the advertiement. + EXPECT_FALSE(found_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_a.StopScanning()); + EXPECT_TRUE(ble_b.StopAdvertising()); + env_.Stop(); +} + +TEST_F(BleV2MediumTest, + CanStartAdvertisingButFastScanningFailToFoundAdvertisement) { + env_.Start(); + BluetoothAdapter adapter_a; + BluetoothAdapter adapter_b; + BleV2Medium ble_a(adapter_a); + BleV2Medium ble_b(adapter_b); + CountDownLatch found_latch(1); + + EXPECT_TRUE(ble_a.StartScanning( + {std::string(kFastAdvertisementServiceUuid)}, kPowerMode, + { + .advertisement_found_cb = + [&found_latch](BleV2Peripheral peripheral, + const BleAdvertisementData& advertisement_data) { + found_latch.CountDown(); + }, + })); + + // Assemble regular advertising and scan response data. + BleAdvertisementData advertising_data = {}; + BleAdvertisementData scan_response_data; + scan_response_data.service_uuids.insert(std::string(kCopresenceServiceUuid)); + scan_response_data.service_data.insert( + {std::string(kCopresenceServiceUuid), + ByteArray(std::string(kAdvertisementString))}); + + EXPECT_TRUE( + ble_b.StartAdvertising(advertising_data, scan_response_data, kPowerMode)); + // Fail to found the advertiement. + EXPECT_FALSE(found_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_a.StopScanning()); + EXPECT_TRUE(ble_b.StopAdvertising()); env_.Stop(); } TEST_F(BleV2MediumTest, CanStartGattServer) { env_.Start(); - BluetoothAdapter adapter_; - BleV2Medium ble{adapter_}; + BluetoothAdapter adapter; + BleV2Medium ble{adapter}; std::string characteristic_uuid = "characteristic_uuid"; - std::unique_ptr gatt_server = ble.StartGattServer({}); + std::unique_ptr gatt_server = + ble.StartGattServer(/*ServerGattConnectionCallback=*/{}); ASSERT_NE(gatt_server, nullptr); - std::vector permissions{ + std::vector permissions = { GattCharacteristic::Permission::kRead}; - std::vector properties{ + std::vector properties = { GattCharacteristic::Property::kRead}; absl::optional gatt_characteristic = gatt_server->CreateCharacteristic(std::string(kCopresenceServiceUuid), @@ -143,21 +249,61 @@ TEST_F(BleV2MediumTest, CanStartGattServer) { env_.Stop(); } -TEST_F(BleV2MediumTest, CanStartScanning) { +TEST_F(BleV2MediumTest, GattClientConnectToGattServerWorks) { env_.Start(); - BluetoothAdapter adapter_; - BleV2Medium ble{adapter_}; + BluetoothAdapter adapter_a; + BluetoothAdapter adapter_b; + BleV2Medium ble_a(adapter_a); + BleV2Medium ble_b(adapter_b); + std::string characteristic_uuid = "characteristic_uuid"; - EXPECT_TRUE(ble.StartScanning( - {std::string(kCopresenceServiceUuid)}, kPowerMode, - { - .advertisement_found_cb = - [](BleV2Peripheral peripheral, - BleAdvertisementData advertisement_data) { - // nothing to do for now - }, - })); - EXPECT_TRUE(ble.StopScanning()); + // Start GattServer + std::unique_ptr gatt_server = + ble_a.StartGattServer(/*ServerGattConnectionCallback=*/{}); + + ASSERT_NE(gatt_server, nullptr); + + std::vector permissions = { + GattCharacteristic::Permission::kRead}; + std::vector properties = { + GattCharacteristic::Property::kRead}; + // Add characteristic and its value. + // NOLINTNEXTLINE(google3-legacy-absl-backports) + absl::optional server_characteristic = + gatt_server->CreateCharacteristic(std::string(kCopresenceServiceUuid), + characteristic_uuid, permissions, + properties); + ASSERT_TRUE(server_characteristic.has_value()); + ByteArray server_value("any"); + EXPECT_TRUE( + gatt_server->UpdateCharacteristic(*server_characteristic, server_value)); + + // Start GattClient + auto ble_peripheral = + std::make_unique(/*mac_address=*/"ABCD"); + std::unique_ptr gatt_client = ble_b.ConnectToGattServer( + BleV2Peripheral(ble_peripheral.get()), kPowerMode, + /*ClientGattConnectionCallback=*/{}); + + ASSERT_NE(gatt_client, nullptr); + + // Discover service. + EXPECT_TRUE( + gatt_client->DiscoverService(std::string(kCopresenceServiceUuid))); + + // Discover characteristic. + // NOLINTNEXTLINE(google3-legacy-absl-backports) + absl::optional client_characteristic = + gatt_client->GetCharacteristic(std::string(kCopresenceServiceUuid), + characteristic_uuid); + ASSERT_TRUE(client_characteristic.has_value()); + + // Can read the characteristic value. + EXPECT_THAT(gatt_client->ReadCharacteristic(*client_characteristic), + Optional(server_value)); + + gatt_client->Disconnect(); + gatt_server->Stop(); env_.Stop(); } diff --git a/internal/platform/implementation/ble_v2.h b/internal/platform/implementation/ble_v2.h index bbc84cba..81a4a370 100644 --- a/internal/platform/implementation/ble_v2.h +++ b/internal/platform/implementation/ble_v2.h @@ -16,6 +16,7 @@ #define PLATFORM_API_BLE_V2_H_ #include +#include #include #include #include @@ -25,7 +26,6 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "internal/platform/byte_array.h" #include "internal/platform/exception.h" #include "internal/platform/input_stream.h" @@ -111,35 +111,32 @@ struct GattCharacteristic { }; std::string uuid; - std::string servie_uuid; + std::string service_uuid; // Hashable template friend H AbslHashValue(H h, const GattCharacteristic& s) { - return H::combine(std::move(h), s.uuid, s.servie_uuid); + return H::combine(std::move(h), s.uuid, s.service_uuid); } bool operator==(const GattCharacteristic& rhs) const { - return this->uuid == rhs.uuid && this->servie_uuid == rhs.servie_uuid; + return this->uuid == rhs.uuid && this->service_uuid == rhs.service_uuid; } }; // https://developer.android.com/reference/android/bluetooth/BluetoothGatt // // Representation of a client GATT connection to a remote GATT server. -class ClientGattConnection { +class GattClient { public: - virtual ~ClientGattConnection() = default; - - // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#getDevice() - virtual BlePeripheral& GetPeripheral() = 0; + virtual ~GattClient() = default; // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#discoverServices() // - // Discovers all available services and characteristics on this connection. + // Discovers available service and characteristics on this connection. // Returns whether or not discovery finished successfully. // // This function should block until discovery has finished. - virtual bool DiscoverServices() = 0; + virtual bool DiscoverService(const std::string& service_uuid) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#getService(java.util.UUID) // https://developer.android.com/reference/android/bluetooth/BluetoothGattService.html#getCharacteristic(java.util.UUID) @@ -172,49 +169,6 @@ class ClientGattConnection { virtual void Disconnect() = 0; }; -// https://developer.android.com/reference/android/bluetooth/BluetoothGattServer -// -// Representation of a server GATT connection to a remote GATT client. -class ServerGattConnection { - public: - virtual ~ServerGattConnection() = default; - - // https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setValue(byte[]) - // https://developer.android.com/reference/android/bluetooth/BluetoothGattServer.html#notifyCharacteristicChanged(android.bluetooth.BluetoothDevice,%20android.bluetooth.BluetoothGattCharacteristic,%20boolean) - // - // Sends a notification (via indication) to the client that a characteristic - // has changed with the given value. Returns whether or not it was - // successful. - // - // The value sent does not have to reflect the locally stored characteristic - // value. To update the local value, call GattServer::UpdateCharacteristic. - virtual bool SendCharacteristic(const GattCharacteristic& characteristic, - const ByteArray& value) = 0; -}; - -// Callback for asynchronous events on the client side of a GATT connection. -struct ClientGattConnectionCallback { - public: - // Called when the client is disconnected from the GATT server. - std::function disconnected_cb = - DefaultCallback(); -}; - -// Callback for asynchronous events on the server side of a GATT connection. -struct ServerGattConnectionCallback { - // Called when a remote peripheral connected to us and subscribed to one of - // our characteristics. - std::function - characteristic_subscription_cb; - - // Called when a remote peripheral unsubscribed from one of our - // characteristics. - std::function - characteristic_unsubscription_cb; -}; - // https://developer.android.com/reference/android/bluetooth/BluetoothGattServer // // Representation of a BLE GATT server. @@ -251,6 +205,26 @@ class GattServer { virtual void Stop() = 0; }; +// Callback for asynchronous events on the client side of a GATT connection. +struct ClientGattConnectionCallback { + public: + // Called when the client is disconnected from the GATT server. + std::function disconnected_cb = DefaultCallback<>(); +}; + +// Callback for asynchronous events on the server side of a GATT connection. +struct ServerGattConnectionCallback { + // Called when a remote peripheral connected to us and subscribed to one of + // our characteristics. + std::function + characteristic_subscription_cb; + + // Called when a remote peripheral unsubscribed from one of our + // characteristics. + std::function + characteristic_unsubscription_cb; +}; + class BleSocket { public: virtual ~BleSocket() {} @@ -295,8 +269,6 @@ class ServerBleSocketLifeCycleCallback : public BleSocketLifeCycleCallback { // for all BLE and GATT related operations. class BleMedium { public: - using Mtu = uint32_t; - virtual ~BleMedium() = default; // https://developer.android.com/reference/android/bluetooth/le/BluetoothLeAdvertiser.html#startAdvertising(android.bluetooth.le.AdvertiseSettings,%20android.bluetooth.le.AdvertiseData,%20android.bluetooth.le.AdvertiseData,%20android.bluetooth.le.AdvertiseCallback) @@ -375,16 +347,13 @@ class BleMedium { // Connects to a GATT server and negotiates the specified connection // parameters. Returns nullptr upon error. // - // Both connection interval and MTU can be negotiated on a best-effort - // basis. - // // Power mode should be interpreted in the following way: // HIGH: // - Connection interval = ~11.25ms - 15ms // LOW: // - Connection interval = ~100ms - 125ms - virtual std::unique_ptr ConnectToGattServer( - BlePeripheral& peripheral, Mtu mtu, PowerMode power_mode, + virtual std::unique_ptr ConnectToGattServer( + BlePeripheral& peripheral, PowerMode power_mode, ClientGattConnectionCallback callback) = 0; // Establishes a BLE socket to the specified remote peripheral. Returns diff --git a/internal/platform/implementation/g3/ble_v2.cc b/internal/platform/implementation/g3/ble_v2.cc index 05e5185b..2c1e5c1b 100644 --- a/internal/platform/implementation/g3/ble_v2.cc +++ b/internal/platform/implementation/g3/ble_v2.cc @@ -16,12 +16,13 @@ #include #include +#include #include #include +#include "absl/strings/escaping.h" #include "absl/synchronization/mutex.h" #include "internal/platform/implementation/ble_v2.h" -#include "internal/platform/implementation/shared/count_down_latch.h" #include "internal/platform/logging.h" #include "internal/platform/medium_environment.h" @@ -34,9 +35,7 @@ namespace { using ::location::nearby::api::ble_v2::BleAdvertisementData; using ::location::nearby::api::ble_v2::BleSocket; using ::location::nearby::api::ble_v2::BleSocketLifeCycleCallback; -using ::location::nearby::api::ble_v2::ClientGattConnection; using ::location::nearby::api::ble_v2::PowerMode; -using ::location::nearby::api::ble_v2::ServerGattConnectionCallback; std::string PowerModeToName(PowerMode power_mode) { switch (power_mode) { @@ -96,7 +95,6 @@ bool BleV2Medium::StartAdvertising( bool BleV2Medium::StopAdvertising() { NEARBY_LOGS(INFO) << "G3 Ble StopAdvertising"; absl::MutexLock lock(&mutex_); - advertisement_byte_ = {}; BleAdvertisementData empty_advertisement_data = {}; MediumEnvironment::Instance().UpdateBleV2MediumForAdvertising( @@ -111,7 +109,7 @@ bool BleV2Medium::StartScanning(const std::vector& service_uuids, absl::MutexLock lock(&mutex_); MediumEnvironment::Instance().UpdateBleV2MediumForScanning( - true, std::move(callback), *this); + /*enabled=*/true, service_uuids.front(), std::move(callback), *this); return true; } @@ -119,12 +117,14 @@ bool BleV2Medium::StopScanning() { NEARBY_LOGS(INFO) << "G3 Ble StopScanning"; absl::MutexLock lock(&mutex_); - MediumEnvironment::Instance().UpdateBleV2MediumForScanning(false, {}, *this); + MediumEnvironment::Instance().UpdateBleV2MediumForScanning( + /*enabled=*/false, + /*service_uuid=*/{}, /*callback=*/{}, *this); return true; } std::unique_ptr BleV2Medium::StartGattServer( - ServerGattConnectionCallback callback) { + api::ble_v2::ServerGattConnectionCallback callback) { return std::make_unique(); } @@ -135,10 +135,10 @@ bool BleV2Medium::StartListeningForIncomingBleSockets( void BleV2Medium::StopListeningForIncomingBleSockets() {} -std::unique_ptr BleV2Medium::ConnectToGattServer( - api::ble_v2::BlePeripheral& peripheral, Mtu mtu, PowerMode power_mode, +std::unique_ptr BleV2Medium::ConnectToGattServer( + api::ble_v2::BlePeripheral& peripheral, PowerMode power_mode, api::ble_v2::ClientGattConnectionCallback callback) { - return nullptr; + return std::make_unique(); } std::unique_ptr BleV2Medium::EstablishBleSocket( @@ -147,6 +147,102 @@ std::unique_ptr BleV2Medium::EstablishBleSocket( return nullptr; } +std::optional +BleV2Medium::GattServer::CreateCharacteristic( + absl::string_view service_uuid, absl::string_view characteristic_uuid, + const std::vector& permissions, + const std::vector& properties) { + api::ble_v2::GattCharacteristic characteristic = { + .uuid = std::string(characteristic_uuid), + .service_uuid = std::string(service_uuid)}; + return characteristic; +} + +bool BleV2Medium::GattServer::UpdateCharacteristic( + const api::ble_v2::GattCharacteristic& characteristic, + const location::nearby::ByteArray& value) { + NEARBY_LOGS(INFO) + << "G3 Ble GattServer UpdateCharacteristic, characteristic=(" + << characteristic.service_uuid << "," << characteristic.uuid + << "), value = " << absl::BytesToHexString(value.data()); + MediumEnvironment::Instance().InsertBleV2MediumGattCharacteristics( + characteristic, value); + return true; +} + +void BleV2Medium::GattServer::Stop() { + NEARBY_LOGS(INFO) << "G3 Ble GattServer Stop"; + MediumEnvironment::Instance().ClearBleV2MediumGattCharacteristics(); +} + +bool BleV2Medium::GattClient::DiscoverService(const std::string& service_uuid) { + absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << "G3 Ble GattClient DiscoverService, service_uuid" + << service_uuid; + if (!is_connection_alive_) { + return false; + } + + // Search if the service exists. + return MediumEnvironment::Instance().ContainsBleV2MediumGattCharacteristics( + service_uuid, ""); +} + +std::optional +BleV2Medium::GattClient::GetCharacteristic( + absl::string_view service_uuid, absl::string_view characteristic_uuid) { + absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << "G3 Ble GattClient GetCharacteristic, service_uuid=" + << service_uuid + << ", characteristic_uuid=" << characteristic_uuid; + if (!is_connection_alive_) { + return std::nullopt; + } + + // Search gatt_characteristic by uuid and if found return the + // gatt_characteristic. + api::ble_v2::GattCharacteristic characteristic = {}; + if (MediumEnvironment::Instance().ContainsBleV2MediumGattCharacteristics( + service_uuid, characteristic_uuid)) { + characteristic = {.uuid = std::string(characteristic_uuid), + .service_uuid = std::string(service_uuid)}; + } + NEARBY_LOGS(INFO) + << "G3 Ble GattClient GetCharacteristic, found characteristic=(" + << characteristic.service_uuid << "," << characteristic.uuid << ")"; + + return characteristic; +} + +std::optional BleV2Medium::GattClient::ReadCharacteristic( + const api::ble_v2::GattCharacteristic& characteristic) { + absl::MutexLock lock(&mutex_); + if (!is_connection_alive_) { + return std::nullopt; + } + + ByteArray value = + MediumEnvironment::Instance().ReadBleV2MediumGattCharacteristics( + characteristic); + NEARBY_LOGS(INFO) << "G3 Ble ReadCharacteristic, characteristic=(" + << characteristic.service_uuid << "," << characteristic.uuid + << "), value = " << absl::BytesToHexString(value.data()); + return std::move(value); +} + +bool BleV2Medium::GattClient::WriteCharacteristic( + const api::ble_v2::GattCharacteristic& characteristic, + const ByteArray& value) { + // No op. + return false; +} + +void BleV2Medium::GattClient::Disconnect() { + absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << "G3 Ble GattClient Disconnect"; + is_connection_alive_ = false; +} + } // namespace g3 } // namespace nearby } // namespace location diff --git a/internal/platform/implementation/g3/ble_v2.h b/internal/platform/implementation/g3/ble_v2.h index 4c849c91..f278dcf4 100644 --- a/internal/platform/implementation/g3/ble_v2.h +++ b/internal/platform/implementation/g3/ble_v2.h @@ -55,9 +55,8 @@ class BleV2Medium : public api::ble_v2::BleMedium { ABSL_LOCKS_EXCLUDED(mutex_); void StopListeningForIncomingBleSockets() override ABSL_LOCKS_EXCLUDED(mutex_); - std::unique_ptr ConnectToGattServer( - api::ble_v2::BlePeripheral& peripheral, Mtu mtu, - api::ble_v2::PowerMode power_mode, + std::unique_ptr ConnectToGattServer( + api::ble_v2::BlePeripheral& peripheral, api::ble_v2::PowerMode power_mode, api::ble_v2::ClientGattConnectionCallback callback) override ABSL_LOCKS_EXCLUDED(mutex_); std::unique_ptr EstablishBleSocket( @@ -68,6 +67,7 @@ class BleV2Medium : public api::ble_v2::BleMedium { BluetoothAdapter& GetAdapter() { return *adapter_; } private: + // A concrete implemenation for GattServer. class GattServer : public api::ble_v2::GattServer { public: std::optional CreateCharacteristic( @@ -75,28 +75,44 @@ class BleV2Medium : public api::ble_v2::BleMedium { const std::vector& permissions, const std::vector& - properties) override { - api::ble_v2::GattCharacteristic characteristic = { - .uuid = std::string(characteristic_uuid), - .servie_uuid = std::string(service_uuid)}; - return characteristic; - } + properties) override; bool UpdateCharacteristic( const api::ble_v2::GattCharacteristic& characteristic, - const location::nearby::ByteArray& value) override { - // No action for now. - return true; - } + const location::nearby::ByteArray& value) override; - void Stop() override { - // No action for now. - } + void Stop() override; + }; + + // A concrete implemenation for GattClient. + class GattClient : public api::ble_v2::GattClient { + public: + bool DiscoverService(const std::string& service_uuid) override; + + std::optional GetCharacteristic( + absl::string_view service_uuid, + absl::string_view characteristic_uuid) override; + + std::optional ReadCharacteristic( + const api::ble_v2::GattCharacteristic& characteristic) override; + + bool WriteCharacteristic( + const api::ble_v2::GattCharacteristic& characteristic, + const ByteArray& value) override; + + void Disconnect() override; + + private: + absl::Mutex mutex_; + + // A flag to indicate the gatt connection alive or not. If it is + // disconnected/*false*/, the instance needs to be created again to bring it + // alive. + bool is_connection_alive_ ABSL_GUARDED_BY(mutex_) = true; }; absl::Mutex mutex_; BluetoothAdapter* adapter_; // Our device adapter; read-only. - ByteArray advertisement_byte_ ABSL_GUARDED_BY(mutex_); }; } // namespace g3 diff --git a/internal/platform/implementation/windows/ble_v2.cc b/internal/platform/implementation/windows/ble_v2.cc index 0df7f29b..4a597b85 100644 --- a/internal/platform/implementation/windows/ble_v2.cc +++ b/internal/platform/implementation/windows/ble_v2.cc @@ -35,7 +35,7 @@ namespace { using ::location::nearby::api::ble_v2::BleAdvertisementData; using ::location::nearby::api::ble_v2::BleSocket; using ::location::nearby::api::ble_v2::BleSocketLifeCycleCallback; -using ::location::nearby::api::ble_v2::ClientGattConnection; +using ::location::nearby::api::ble_v2::GattClient; using ::location::nearby::api::ble_v2::PowerMode; using ::location::nearby::api::ble_v2::ServerGattConnectionCallback; using ::winrt::Windows::Devices::Bluetooth::BluetoothError; @@ -289,8 +289,8 @@ bool BleV2Medium::StartListeningForIncomingBleSockets( void BleV2Medium::StopListeningForIncomingBleSockets() {} -std::unique_ptr BleV2Medium::ConnectToGattServer( - api::ble_v2::BlePeripheral& peripheral, Mtu mtu, PowerMode power_mode, +std::unique_ptr BleV2Medium::ConnectToGattServer( + api::ble_v2::BlePeripheral& peripheral, PowerMode power_mode, api::ble_v2::ClientGattConnectionCallback callback) { return nullptr; } diff --git a/internal/platform/implementation/windows/ble_v2.h b/internal/platform/implementation/windows/ble_v2.h index 2feec17a..c443d5f9 100644 --- a/internal/platform/implementation/windows/ble_v2.h +++ b/internal/platform/implementation/windows/ble_v2.h @@ -66,9 +66,8 @@ class BleV2Medium : public api::ble_v2::BleMedium { ABSL_LOCKS_EXCLUDED(mutex_); void StopListeningForIncomingBleSockets() override ABSL_LOCKS_EXCLUDED(mutex_); - std::unique_ptr ConnectToGattServer( - api::ble_v2::BlePeripheral& peripheral, Mtu mtu, - api::ble_v2::PowerMode power_mode, + std::unique_ptr ConnectToGattServer( + api::ble_v2::BlePeripheral& peripheral, api::ble_v2::PowerMode power_mode, api::ble_v2::ClientGattConnectionCallback callback) override ABSL_LOCKS_EXCLUDED(mutex_); std::unique_ptr EstablishBleSocket( diff --git a/internal/platform/medium_environment.cc b/internal/platform/medium_environment.cc index 920579e7..fb8f0205 100644 --- a/internal/platform/medium_environment.cc +++ b/internal/platform/medium_environment.cc @@ -539,6 +539,9 @@ void MediumEnvironment::UpdateBleV2MediumForAdvertising( BleV2MediumContext& remote_context = medium_info.second; // Do not send notification to the same medium. if (remote_medium == &medium) continue; + if (!context.advertisement_data.service_uuids.contains( + remote_context.scanning_service_uuid)) + continue; NEARBY_LOGS(INFO) << "G3 UpdateBleV2MediumForAdvertising, found other medium=" << remote_medium << ", remote_medium_context=" << &remote_context @@ -552,10 +555,12 @@ void MediumEnvironment::UpdateBleV2MediumForAdvertising( } void MediumEnvironment::UpdateBleV2MediumForScanning( - bool enabled, BleScanCallback callback, api::ble_v2::BleMedium& medium) { + bool enabled, const std::string& scanning_service_uuid, + BleScanCallback callback, api::ble_v2::BleMedium& medium) { if (!enabled_) return; RunOnMediumEnvironmentThread( - [this, &medium, callback = std::move(callback), enabled]() { + [this, &medium, scanning_service_uuid = scanning_service_uuid, + callback = std::move(callback), enabled]() { auto it = ble_v2_mediums_.find(&medium); if (it == ble_v2_mediums_.end()) { NEARBY_LOGS(INFO) @@ -565,6 +570,7 @@ void MediumEnvironment::UpdateBleV2MediumForScanning( } BleV2MediumContext& context = it->second; context.scan_callback = std::move(callback); + context.scanning_service_uuid = scanning_service_uuid; NEARBY_LOGS(INFO) << "G3 UpdateBleV2MediumForScanning: this=" << this << ", medium=" << &medium << ", medium_context=" << &context @@ -577,10 +583,14 @@ void MediumEnvironment::UpdateBleV2MediumForScanning( // medium. if (remote_medium == &medium || !remote_context.advertising) continue; + if (!remote_context.advertisement_data.service_uuids.contains( + context.scanning_service_uuid)) + continue; NEARBY_LOGS(INFO) << "G3 UpdateBleV2MediumForScanning, found other medium=" << remote_medium << ", remote_medium_context=" << &remote_context + << ", scanning_service_uuid=" << scanning_service_uuid << ". Ready to call OnBleV2PeripheralStateChanged."; OnBleV2PeripheralStateChanged(enabled, context, remote_context.advertisement_data, @@ -590,6 +600,68 @@ void MediumEnvironment::UpdateBleV2MediumForScanning( }); } +void MediumEnvironment::InsertBleV2MediumGattCharacteristics( + const api::ble_v2::GattCharacteristic& characteristic, + const ByteArray& gatt_advertisement_byte) { + if (!enabled_) return; + CountDownLatch latch(1); + RunOnMediumEnvironmentThread( + [this, &latch, &characteristic, &gatt_advertisement_byte]() { + gatt_advertisement_bytes_[characteristic] = gatt_advertisement_byte; + latch.CountDown(); + }); + latch.Await(); +} + +bool MediumEnvironment::ContainsBleV2MediumGattCharacteristics( + absl::string_view service_uuid, absl::string_view characteristic_uuid) { + if (!enabled_) return false; + bool found_characteristic = false; + CountDownLatch latch(1); + RunOnMediumEnvironmentThread([this, &latch, &service_uuid, + &characteristic_uuid, &found_characteristic]() { + for (const auto& item : gatt_advertisement_bytes_) { + if (service_uuid == item.first.service_uuid) { + if (characteristic_uuid.empty()) { + // Found the service uuid and no need to search characteristic + // uuid. + found_characteristic = true; + break; + } + if (characteristic_uuid == item.first.uuid) { + found_characteristic = true; + break; + } + } + } + latch.CountDown(); + }); + latch.Await(); + return found_characteristic; +} + +ByteArray MediumEnvironment::ReadBleV2MediumGattCharacteristics( + const api::ble_v2::GattCharacteristic& characteristic) { + if (!enabled_) return {}; + ByteArray gatt_advertisement_byte = {}; + CountDownLatch latch(1); + RunOnMediumEnvironmentThread( + [this, &latch, &characteristic, &gatt_advertisement_byte]() { + auto it = gatt_advertisement_bytes_.find(characteristic); + if (it != gatt_advertisement_bytes_.end()) { + gatt_advertisement_byte = it->second; + } + latch.CountDown(); + }); + latch.Await(); + return gatt_advertisement_byte; +} + +void MediumEnvironment::ClearBleV2MediumGattCharacteristics() { + if (!enabled_) return; + RunOnMediumEnvironmentThread([this]() { gatt_advertisement_bytes_.clear(); }); +} + void MediumEnvironment::UnregisterBleV2Medium(api::ble_v2::BleMedium& medium) { if (!enabled_) return; RunOnMediumEnvironmentThread([this, &medium]() { diff --git a/internal/platform/medium_environment.h b/internal/platform/medium_environment.h index fb43ef03..abeef98c 100644 --- a/internal/platform/medium_environment.h +++ b/internal/platform/medium_environment.h @@ -230,9 +230,31 @@ class MediumEnvironment { // This should be called when discoverable state changes. // The `callback` argument should be non-empty if `enabled` is true or empty // if `enabled` is false. - void UpdateBleV2MediumForScanning(bool enabled, BleScanCallback callback, + void UpdateBleV2MediumForScanning(bool enabled, + const std::string& scanning_service_uuid, + BleScanCallback callback, api::ble_v2::BleMedium& medium); + // Inserts the BLE GATT characteristic and its value BleAdvertisement byte + // array. + void InsertBleV2MediumGattCharacteristics( + const api::ble_v2::GattCharacteristic& characteristic, + const ByteArray& gatt_advertisement_byte); + + // Check if `service_uuid` and `characteristic_uuid` exists in the map. + // + // `characteristic_uuid` can be empty and to check `service_uuid` only. + bool ContainsBleV2MediumGattCharacteristics( + absl::string_view service_uuid, absl::string_view characteristic_uuid); + + // Reads the BLE GATT characteristic value. If the GATT characteristic is not + // existed, return empty byte array. + ByteArray ReadBleV2MediumGattCharacteristics( + const api::ble_v2::GattCharacteristic& characteristic); + + // Clears the map `gatt_advertisement_bytes_`. + void ClearBleV2MediumGattCharacteristics(); + // Removes medium-related info. This should correspond to device power off. void UnregisterBleV2Medium(api::ble_v2::BleMedium& mediumum); @@ -311,6 +333,7 @@ class MediumEnvironment { BleScanCallback scan_callback = {}; api::ble_v2::BlePeripheral* ble_peripheral = nullptr; api::ble_v2::BleAdvertisementData advertisement_data; + std::string scanning_service_uuid = {}; bool advertising = false; }; @@ -377,6 +400,9 @@ class MediumEnvironment { absl::flat_hash_map ble_mediums_; absl::flat_hash_map ble_v2_mediums_; + absl::flat_hash_map + gatt_advertisement_bytes_; #ifndef NO_WEBRTC // Maps peer id to callback for receiving signaling messages.