From 190d3ebafcc915d492c1c514738aca555f746caa Mon Sep 17 00:00:00 2001 From: edwinwu Date: Wed, 30 Mar 2022 07:05:35 -0700 Subject: [PATCH] [BLE Refactor] Impl Start/Stop Scanning and DiscoveredPeripheral callback function in medium layer and ScanCallback in platform layer. PiperOrigin-RevId: 438293171 --- connections/implementation/mediums/BUILD | 1 + connections/implementation/mediums/ble_v2.cc | 133 +++++++++++++++--- connections/implementation/mediums/ble_v2.h | 45 ++++-- .../mediums/ble_v2/ble_peripheral.h | 9 +- .../implementation/mediums/ble_v2_test.cc | 81 +++++++++-- internal/platform/ble_v2.cc | 55 ++++++++ internal/platform/ble_v2.h | 26 ++++ internal/platform/ble_v2_test.cc | 22 ++- internal/platform/bluetooth_adapter.h | 20 +++ internal/platform/implementation/ble_v2.h | 10 +- internal/platform/implementation/g3/ble_v2.cc | 43 +++--- .../implementation/g3/bluetooth_adapter.cc | 8 +- .../implementation/g3/bluetooth_adapter.h | 18 +++ internal/platform/medium_environment.cc | 126 ++++++++++++----- internal/platform/medium_environment.h | 55 +++++--- 15 files changed, 520 insertions(+), 132 deletions(-) diff --git a/connections/implementation/mediums/BUILD b/connections/implementation/mediums/BUILD index cf4770c8..3cb5f8ab 100644 --- a/connections/implementation/mediums/BUILD +++ b/connections/implementation/mediums/BUILD @@ -102,6 +102,7 @@ cc_test( deps = [ ":mediums", ":utils", + "//connections/implementation/mediums/ble_v2", "//internal/platform:base", "//internal/platform:comm", "//internal/platform:test_util", diff --git a/connections/implementation/mediums/ble_v2.cc b/connections/implementation/mediums/ble_v2.cc index 81fcc0f6..696c6b9d 100644 --- a/connections/implementation/mediums/ble_v2.cc +++ b/connections/implementation/mediums/ble_v2.cc @@ -207,23 +207,8 @@ bool BleV2::StartAdvertising( {std::string(kCopresenceServiceUuid), advertisement_header_bytes}); } - // Convert power_mode from power_level. - PowerMode power_mode = PowerMode::kUnknown; - switch (power_level) { - case PowerLevel::kHighPower: - power_mode = PowerMode::kHigh; - break; - case PowerLevel::kLowPower: - // Medium power is about the size of a conference room. Any lower power - // it won't be visible at a distance. - power_mode = PowerMode::kMedium; - break; - default: - power_mode = PowerMode::kUnknown; - } - if (!medium_.StartAdvertising(advertising_data, scan_response_data, - power_mode)) { + PowerLevelToPowerMode(power_level))) { NEARBY_LOGS(ERROR) << "Failed to turn on BLE advertising with advertisement bytes=" << absl::BytesToHexString(advertisement_bytes.data()) @@ -239,7 +224,7 @@ bool BleV2::StartAdvertising( NEARBY_LOGS(INFO) << "Started BLE advertising with advertisement bytes=" << absl::BytesToHexString(advertisement_bytes.data()) << " for service_id=" << service_id; - advertising_info_.Add(service_id); + advertising_service_ids_.insert(service_id); return true; } @@ -281,7 +266,7 @@ bool BleV2::StopAdvertising(const std::string& service_id) { NEARBY_LOGS(INFO) << "Turned off BLE advertising with service_id=" << service_id; - advertising_info_.Remove(service_id); + advertising_service_ids_.erase(service_id); return medium_.StopAdvertising(); } @@ -291,10 +276,107 @@ bool BleV2::IsAdvertising(const std::string& service_id) const { return IsAdvertisingLocked(service_id); } +bool BleV2::StartScanning(const std::string& service_id, PowerLevel power_level, + DiscoveredPeripheralCallback callback, + const std::string& fast_advertisement_service_uuid) { + MutexLock lock(&mutex_); + + if (service_id.empty()) { + NEARBY_LOGS(INFO) << "Can not start BLE scanning with empty service id."; + return false; + } + + if (IsScanningLocked(service_id)) { + NEARBY_LOGS(INFO) << "Cannot start scan of BLE peripherals because " + "scanning is already in-progress."; + return false; + } + + if (!radio_.IsEnabled()) { + NEARBY_LOGS(INFO) + << "Can't start BLE scanning because Bluetooth is disabled"; + return false; + } + + if (!IsAvailableLocked()) { + NEARBY_LOGS(INFO) + << "Can't scan BLE peripherals because BLE isn't available."; + return false; + } + + // TODO(edwinwu): Start discovered peripheral tracking. + + // 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(kCopresenceServiceUuid)}; + if (!medium_.StartScanning( + service_uuids, PowerLevelToPowerMode(power_level), + { + .advertisement_found_cb = + [](BleV2Peripheral peripheral, + const BleAdvertisementData& advertisement_data) { + // TODO(b/213835576): Move (or Copy at fallback) the + // BleV2Peripheral. + // TODO(b/216629800): Track the found advertisement. + }, + })) { + NEARBY_LOGS(INFO) << "Failed to start client scan of BLE services."; + // Erase the service id that is just added. + scanned_service_ids_.erase(service_id); + return false; + } + + NEARBY_LOGS(INFO) << "Turned on BLE scanning with service id=" << service_id; + return true; +} + +bool BleV2::StopScanning(const std::string& service_id) { + MutexLock lock(&mutex_); + + if (!IsScanningLocked(service_id)) { + NEARBY_LOGS(INFO) << "Can't turn off BLE scanning because we never " + "started scanning."; + return false; + } + + // TODO(b/213835576): Cancel lost alarm and Stop tracking. + + scanned_service_ids_.erase(service_id); + NEARBY_LOGS(INFO) << "Turned off BLE scanning with service id=" << service_id; + + // If still has scanner, don't stop the client scanning. + if (!scanned_service_ids_.empty()) { + return true; + } + + NEARBY_LOGS(INFO) << "Turned off BLE client scanning"; + return medium_.StopScanning(); +} + +bool BleV2::IsScanning(const std::string& service_id) const { + MutexLock lock(&mutex_); + + return IsScanningLocked(service_id); +} + bool BleV2::IsAvailableLocked() const { return medium_.IsValid(); } bool BleV2::IsAdvertisingLocked(const std::string& service_id) const { - return advertising_info_.Existed(service_id); + return advertising_service_ids_.contains(service_id); +} + +bool BleV2::IsScanningLocked(const std::string& service_id) const { + return scanned_service_ids_.contains(service_id); } bool BleV2::IsAdvertisementGattServerRunningLocked() { @@ -422,6 +504,19 @@ std::string BleV2::GenerateAdvertisementUuid(int slot) { return std::string(Uuid(kAdvertisementUuidMsb, kAdvertisementUuidLsb | slot)); } +PowerMode BleV2::PowerLevelToPowerMode(PowerLevel power_level) { + switch (power_level) { + case PowerLevel::kHighPower: + return PowerMode::kHigh; + case PowerLevel::kLowPower: + // Medium power is about the size of a conference room. + // Any lower and we won't be visible at a distance. + return PowerMode::kMedium; + default: + return PowerMode::kUnknown; + } +} + } // namespace connections } // namespace nearby } // namespace location diff --git a/connections/implementation/mediums/ble_v2.h b/connections/implementation/mediums/ble_v2.h index 81e56e25..5c6fa6ac 100644 --- a/connections/implementation/mediums/ble_v2.h +++ b/connections/implementation/mediums/ble_v2.h @@ -24,6 +24,7 @@ #include "absl/container/flat_hash_set.h" #include "absl/strings/string_view.h" #include "connections/advertising_options.h" +#include "connections/implementation/mediums/ble_v2/discovered_peripheral_callback.h" #include "connections/implementation/mediums/bluetooth_radio.h" #include "internal/platform/ble_v2.h" #include "internal/platform/byte_array.h" @@ -38,6 +39,10 @@ namespace connections { // (BLE) medium. class BleV2 final { public: + using ServerGattConnectionCallback = + BleV2Medium::ServerGattConnectionCallback; + using DiscoveredPeripheralCallback = mediums::DiscoveredPeripheralCallback; + explicit BleV2(BluetoothRadio& bluetooth_radio); // Returns true, if BLE communications are supported by a platform. @@ -58,6 +63,22 @@ class BleV2 final { bool IsAdvertising(const std::string& service_id) const ABSL_LOCKS_EXCLUDED(mutex_); + // Enables BLE scanning for a service id. Will report any discoverable + // advertisement data through a callback. + // Returns true, if the scanning is successfully enabled, false otherwise. + bool StartScanning(const std::string& service_id, PowerLevel power_level, + DiscoveredPeripheralCallback callback, + const std::string& fast_advertisement_service_uuid) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Disables BLE scanning for a service id. + // Returns true, if the scanning was previously enabled, false otherwise. + bool StopScanning(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true if the scanning for service id is enabled. + bool IsScanning(const std::string& service_id) const + ABSL_LOCKS_EXCLUDED(mutex_); + // Returns true if this object owns a valid platform implementation. bool IsMediumValid() const ABSL_LOCKS_EXCLUDED(mutex_) { MutexLock lock(&mutex_); @@ -65,20 +86,6 @@ class BleV2 final { } private: - struct AdvertisingInfo { - bool Empty() const { return service_ids.empty(); } - void Clear() { service_ids.clear(); } - void Add(const std::string& service_id) { service_ids.insert(service_id); } - void Remove(const std::string& service_id) { - service_ids.erase(service_id); - } - bool Existed(const std::string& service_id) const { - return service_ids.contains(service_id); - } - - absl::flat_hash_set service_ids; - }; - // Same as IsAvailable(), but must be called with `mutex_` held. bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); @@ -86,6 +93,10 @@ class BleV2 final { bool IsAdvertisingLocked(const std::string& service_id) const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + // Same as IsScanning(), but must be called with `mutex_` held. + bool IsScanningLocked(const std::string& service_id) const + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + bool IsAdvertisementGattServerRunningLocked() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); bool StartAdvertisementGattServerLocked(const std::string& service_id, @@ -101,16 +112,20 @@ class BleV2 final { ByteArray CreateAdvertisementHeader() ABSL_SHARED_LOCKS_REQUIRED(mutex_); std::string GenerateAdvertisementUuid(int slot); + api::ble_v2::PowerMode PowerLevelToPowerMode(PowerLevel power_level); + mutable Mutex mutex_; BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_); BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_); BleV2Medium medium_ ABSL_GUARDED_BY(mutex_){adapter_}; - AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_); + absl::flat_hash_set advertising_service_ids_ + ABSL_GUARDED_BY(mutex_); std::unique_ptr gatt_server_ ABSL_GUARDED_BY(mutex_); absl::flat_hash_map> gatt_advertisements_ ABSL_GUARDED_BY(mutex_); absl::flat_hash_set subscribed_gatt_characteristics_ ABSL_GUARDED_BY(mutex_); + absl::flat_hash_set scanned_service_ids_ ABSL_GUARDED_BY(mutex_); }; } // namespace connections diff --git a/connections/implementation/mediums/ble_v2/ble_peripheral.h b/connections/implementation/mediums/ble_v2/ble_peripheral.h index e3ef5c36..e8c05287 100644 --- a/connections/implementation/mediums/ble_v2/ble_peripheral.h +++ b/connections/implementation/mediums/ble_v2/ble_peripheral.h @@ -22,6 +22,11 @@ namespace nearby { namespace connections { namespace mediums { +// TODO(b/213835576): The peripheral class is for NearbyConnections to transmit +// "advertisement byte array" when peripheral discovered in +// 'discovered_peripheral_track' class, which is not the same as the one in +// BluetoothAdapter. We need to see how to differentiate between this +// BlePeripheral and BleV2Peripheral in BluetoothAdapter. class BlePeripheral { public: BlePeripheral() = default; @@ -36,8 +41,8 @@ class BlePeripheral { ByteArray GetId() const { return id_; } private: - // A unique identifier for this peripheral. It can be the BLE advertisement it - // was found on, or even simply the BLE MAC address. + // A unique identifier for this peripheral. It is the BLE advertisement it + // was found on. ByteArray id_; }; diff --git a/connections/implementation/mediums/ble_v2_test.cc b/connections/implementation/mediums/ble_v2_test.cc index f7cabcdc..4b5503f1 100644 --- a/connections/implementation/mediums/ble_v2_test.cc +++ b/connections/implementation/mediums/ble_v2_test.cc @@ -19,6 +19,7 @@ #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" +#include "connections/implementation/mediums/ble_v2/discovered_peripheral_callback.h" #include "connections/implementation/mediums/bluetooth_radio.h" #include "internal/platform/ble.h" #include "internal/platform/count_down_latch.h" @@ -30,7 +31,10 @@ namespace nearby { namespace connections { namespace { -constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"}; +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{"FAST"}; @@ -61,18 +65,16 @@ TEST_F(BleV2Test, CanStartFastAdvertising) { BluetoothRadio radio; BleV2 ble{radio}; radio.Enable(); - std::string service_id(kServiceID); ByteArray advertisement_bytes{std::string(kAdvertisementString)}; - std::string fast_advertisement_service_uuid(kFastAdvertisementServiceUuid); - PowerLevel power_level = PowerLevel::kHighPower; - EXPECT_TRUE(ble.StartAdvertising(service_id, advertisement_bytes, power_level, - fast_advertisement_service_uuid)); + EXPECT_TRUE(ble.StartAdvertising(std::string(kServiceIDA), + advertisement_bytes, PowerLevel::kHighPower, + std::string(kFastAdvertisementServiceUuid))); // Can't advertise twice for the same service_id. - EXPECT_FALSE(ble.StartAdvertising(service_id, advertisement_bytes, - power_level, - fast_advertisement_service_uuid)); - EXPECT_TRUE(ble.StopAdvertising(service_id)); + EXPECT_FALSE(ble.StartAdvertising( + std::string(kServiceIDA), advertisement_bytes, PowerLevel::kHighPower, + std::string(kFastAdvertisementServiceUuid))); + EXPECT_TRUE(ble.StopAdvertising(std::string(kServiceIDA))); env_.Stop(); } @@ -81,14 +83,65 @@ TEST_F(BleV2Test, CanStartAdvertising) { BluetoothRadio radio; BleV2 ble{radio}; radio.Enable(); - std::string service_id(kServiceID); ByteArray advertisement_bytes{std::string(kAdvertisementString)}; std::string no_fast_advertisement_service_uuid = {}; - PowerLevel power_level = PowerLevel::kHighPower; - EXPECT_TRUE(ble.StartAdvertising(service_id, advertisement_bytes, power_level, + EXPECT_TRUE(ble.StartAdvertising(std::string(kServiceIDA), + advertisement_bytes, PowerLevel::kHighPower, no_fast_advertisement_service_uuid)); - EXPECT_TRUE(ble.StopAdvertising(service_id)); + EXPECT_TRUE(ble.StopAdvertising(std::string(kServiceIDA))); + env_.Stop(); +} + +TEST_F(BleV2Test, CanStartDiscovery) { + env_.Start(); + BluetoothRadio radio; + BleV2 ble{radio}; + radio.Enable(); + + EXPECT_TRUE(ble.StartScanning(std::string(kServiceIDA), + PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [](mediums::BlePeripheral& peripheral, + const std::string& service_id, + const ByteArray& advertisement_bytes, + bool fast_advertisement) { + // nothing to do for now + }, + .peripheral_lost_cb = + [](mediums::BlePeripheral& peripheral, + const std::string& service_id) { + // nothing to do for now + }, + }, + std::string(kFastAdvertisementServiceUuid))); + EXPECT_TRUE(ble.StopScanning(std::string(kServiceIDA))); + env_.Stop(); +} + +TEST_F(BleV2Test, CanStartStopMultipleScanningWithDifferentServiceIds) { + env_.Start(); + BluetoothRadio radio; + BleV2 ble{radio}; + radio.Enable(); + + EXPECT_TRUE(ble.StartScanning(std::string(kServiceIDA), + PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{}, + std::string(kFastAdvertisementServiceUuid))); + EXPECT_TRUE(ble.StartScanning(std::string(kServiceIDB), + PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{}, + std::string(kFastAdvertisementServiceUuid))); + EXPECT_TRUE(ble.StopScanning(std::string(kServiceIDA))); + + EXPECT_TRUE(ble.StartScanning(std::string(kServiceIDA), + PowerLevel::kHighPower, + mediums::DiscoveredPeripheralCallback{}, + std::string(kFastAdvertisementServiceUuid))); + EXPECT_TRUE(ble.StopScanning(std::string(kServiceIDA))); + EXPECT_TRUE(ble.StopScanning(std::string(kServiceIDB))); env_.Stop(); } diff --git a/internal/platform/ble_v2.cc b/internal/platform/ble_v2.cc index 3f2aea20..e00cb73b 100644 --- a/internal/platform/ble_v2.cc +++ b/internal/platform/ble_v2.cc @@ -16,6 +16,7 @@ #include +#include "internal/platform/bluetooth_adapter.h" #include "internal/platform/implementation/ble_v2.h" #include "internal/platform/logging.h" #include "internal/platform/mutex_lock.h" @@ -36,6 +37,60 @@ bool BleV2Medium::StartAdvertising( bool BleV2Medium::StopAdvertising() { return impl_->StopAdvertising(); } +bool BleV2Medium::StartScanning(const std::vector& service_uuids, + PowerMode power_mode, ScanCallback callback) { + MutexLock lock(&mutex_); + if (scanning_enabled_) { + NEARBY_LOGS(INFO) << "Ble Scanning already enabled; impl=" << GetImpl(); + return false; + } + bool success = impl_->StartScanning( + service_uuids, power_mode, + { + .advertisement_found_cb = + [this](api::ble_v2::BlePeripheral& peripheral, + const BleAdvertisementData& advertisement_data) { + MutexLock lock(&mutex_); + if (peripherals_.contains(&peripheral)) { + NEARBY_LOGS(INFO) + << "There is no need to callback due to peripheral impl=" + << &peripheral << ", which already exists."; + return; + } + peripherals_.insert(&peripheral); + BleV2Peripheral proxy(&peripheral); + NEARBY_LOGS(INFO) + << "New peripheral imp=" << &peripheral + << ", callback the proxy peripheral=" << &proxy; + if (!scanning_enabled_) return; + scan_callback_.advertisement_found_cb(std::move(proxy), + advertisement_data); + }, + }); + if (success) { + scan_callback_ = std::move(callback); + // Clear the `peripherals_` after succeeded in StartScanning and before the + // advertisement_found callback has been reached. This prevents deleting the + // existing `peripherals_` if the scanning is not started successfully. If + // sanning is started successfully, we need to clear `peripherals_` to + // prevent the stale data in cache. + peripherals_.clear(); + scanning_enabled_ = true; + NEARBY_LOG(INFO, "Ble Scanning enabled; impl=%p", GetImpl()); + } + return success; +} + +bool BleV2Medium::StopScanning() { + MutexLock lock(&mutex_); + if (!scanning_enabled_) return true; + scanning_enabled_ = false; + peripherals_.clear(); + scan_callback_ = {}; + NEARBY_LOG(INFO, "Ble Scanning disabled: impl=%p", GetImpl()); + return impl_->StopScanning(); +} + std::unique_ptr BleV2Medium::StartGattServer( ServerGattConnectionCallback callback) { { diff --git a/internal/platform/ble_v2.h b/internal/platform/ble_v2.h index 915898bb..9922436f 100644 --- a/internal/platform/ble_v2.h +++ b/internal/platform/ble_v2.h @@ -16,6 +16,7 @@ #define PLATFORM_PUBLIC_BLE_V2_H_ #include + #include "absl/container/flat_hash_map.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/byte_array.h" @@ -94,6 +95,22 @@ class GattServer final { // Container of operations that can be performed over the BLE medium. class BleV2Medium final { public: + // A wrapper callback for BLE scan results. + // + // The peripheral is a wrapper object which stores the real impl of + // api::BlePeripheral. + // The reference will remain valid while api::BlePeripheral object is + // itself valid. Typically peripheral lifetime matches duration of the + // connection, and is controlled by primitive client, since they hold the + // instance. + struct ScanCallback { + std::function + advertisement_found_cb = location::nearby::DefaultCallback< + BleV2Peripheral, const api::ble_v2::BleAdvertisementData&>(); + }; + struct ServerGattConnectionCallback { std::function @@ -117,6 +134,11 @@ class BleV2Medium final { api::ble_v2::PowerMode power_mode); bool StopAdvertising(); + // Returns true once the BLE scan has been initiated. + bool StartScanning(const std::vector& service_uuids, + api::ble_v2::PowerMode power_mode, ScanCallback callback); + bool StopScanning(); + std::unique_ptr StartGattServer( ServerGattConnectionCallback callback); @@ -130,6 +152,10 @@ class BleV2Medium final { BluetoothAdapter& adapter_; ServerGattConnectionCallback server_gatt_connection_callback_ ABSL_GUARDED_BY(mutex_); + absl::flat_hash_set peripherals_ + ABSL_GUARDED_BY(mutex_); + ScanCallback scan_callback_ ABSL_GUARDED_BY(mutex_); + bool scanning_enabled_ ABSL_GUARDED_BY(mutex_) = false; }; } // namespace nearby diff --git a/internal/platform/ble_v2_test.cc b/internal/platform/ble_v2_test.cc index 0e13d3e3..32272c78 100644 --- a/internal/platform/ble_v2_test.cc +++ b/internal/platform/ble_v2_test.cc @@ -30,7 +30,7 @@ using ::location::nearby::api::ble_v2::GattCharacteristic; using ::location::nearby::api::ble_v2::PowerMode; constexpr absl::string_view kAdvertisementString{"\x0a\x0b\x0c\x0d"}; -constexpr absl::string_view kCoprsenseServiceUuid{"F3FE"}; +constexpr absl::string_view kCopresenceServiceUuid{"F3FE"}; constexpr absl::string_view kFastAdvertisementServiceUuid{"FAST"}; constexpr PowerMode kPowerMode(PowerMode::kHigh); @@ -128,7 +128,7 @@ TEST_F(BleV2MediumTest, CanStartGattServer) { std::vector properties{ GattCharacteristic::Property::kRead}; absl::optional gatt_characteristic = - gatt_server->CreateCharacteristic(std::string(kCoprsenseServiceUuid), + gatt_server->CreateCharacteristic(std::string(kCopresenceServiceUuid), characteristic_uuid, permissions, properties); @@ -144,6 +144,24 @@ TEST_F(BleV2MediumTest, CanStartGattServer) { env_.Stop(); } +TEST_F(BleV2MediumTest, CanStartScanning) { + env_.Start(); + BluetoothAdapter adapter_; + BleV2Medium ble{adapter_}; + + EXPECT_TRUE(ble.StartScanning( + {std::string(kCopresenceServiceUuid)}, kPowerMode, + { + .advertisement_found_cb = + [](BleV2Peripheral peripheral, + const BleAdvertisementData& advertisement_data) { + // nothing to do for now + }, + })); + EXPECT_TRUE(ble.StopScanning()); + env_.Stop(); +} + } // namespace } // namespace nearby } // namespace location diff --git a/internal/platform/bluetooth_adapter.h b/internal/platform/bluetooth_adapter.h index 903e1cc2..3545206c 100644 --- a/internal/platform/bluetooth_adapter.h +++ b/internal/platform/bluetooth_adapter.h @@ -48,6 +48,26 @@ class BlePeripheral final { api::BlePeripheral* impl_; }; +// Opaque wrapper over a BLE peripheral. +class BleV2Peripheral final { + public: + BleV2Peripheral() = default; + BleV2Peripheral(const BleV2Peripheral&) = default; + BleV2Peripheral& operator=(const BleV2Peripheral&) = default; + explicit BleV2Peripheral(api::ble_v2::BlePeripheral* peripheral) + : impl_(peripheral) {} + + std::string GetId() const { return impl_->GetId(); } + + // Returns reference to platform implementation. + // This is used to communicate with platform code, and for debugging purposes. + api::ble_v2::BlePeripheral& GetImpl() { return *impl_; } + bool IsValid() const { return impl_ != nullptr; } + + private: + api::ble_v2::BlePeripheral* impl_; +}; + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html. class BluetoothDevice final { public: diff --git a/internal/platform/implementation/ble_v2.h b/internal/platform/implementation/ble_v2.h index 16cf981b..937393e6 100644 --- a/internal/platform/implementation/ble_v2.h +++ b/internal/platform/implementation/ble_v2.h @@ -329,11 +329,13 @@ class BleMedium { // Every discovery of an advertisement should be reported, even if the // advertisement was discovered before. // - // Ownership of the BleAdvertisementData transfers to the caller at this - // point. + // The peripheral is owned by platform implementation and it should outlive + // for the whole peripheral(device) connection life cycle. struct ScanCallback { - std::function - advertisement_found_cb = DefaultCallback(); + std::function + advertisement_found_cb = + DefaultCallback(); }; // https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html#startScan(java.util.List%3Candroid.bluetooth.le.ScanFilter%3E,%20android.bluetooth.le.ScanSettings,%20android.bluetooth.le.ScanCallback) diff --git a/internal/platform/implementation/g3/ble_v2.cc b/internal/platform/implementation/g3/ble_v2.cc index 0c8672f8..11e17c50 100644 --- a/internal/platform/implementation/g3/ble_v2.cc +++ b/internal/platform/implementation/g3/ble_v2.cc @@ -76,21 +76,8 @@ bool BleV2Medium::StartAdvertising( << ", power_mode=" << PowerModeToName(power_mode); absl::MutexLock lock(&mutex_); - auto& env = MediumEnvironment::Instance(); - // If `advertising_data.service_uuids` is empty, then it is fast - // advertisement. In real case, this should be the CopresenceServiceUuid or - // the fastAdvertisementUuid. - bool is_fast_advertisement = !advertising_data.service_uuids.empty(); - for (const auto& service_data : scan_response_data.service_data) { - // Interested item found in the first index. - advertisement_byte_ = service_data.second; - if (!advertisement_byte_.Empty()) { - break; - } - } - if (advertisement_byte_.Empty()) return false; - env.UpdateBleV2MediumForAdvertising(is_fast_advertisement, true, *this, - &advertisement_byte_); + MediumEnvironment::Instance().UpdateBleV2MediumForAdvertising( + /*enabled=*/true, *this, adapter_->GetPeripheralV2(), scan_response_data); return true; } @@ -99,20 +86,30 @@ bool BleV2Medium::StopAdvertising() { absl::MutexLock lock(&mutex_); advertisement_byte_ = {}; - auto& env = MediumEnvironment::Instance(); - env.UpdateBleV2MediumForAdvertising( - /*is_fast_advertisement=*/false, - /*enabled=*/false, *this, &advertisement_byte_); + BleAdvertisementData empty_advertisement_data = {}; + MediumEnvironment::Instance().UpdateBleV2MediumForAdvertising( + /*enabled=*/false, *this, /*mutable=*/adapter_->GetPeripheralV2(), + empty_advertisement_data); return true; } bool BleV2Medium::StartScanning(const std::vector& service_uuids, - PowerMode power_mode, - ScanCallback scan_callback) { - return false; + PowerMode power_mode, ScanCallback callback) { + NEARBY_LOGS(INFO) << "G3 Ble StartScanning"; + absl::MutexLock lock(&mutex_); + + MediumEnvironment::Instance().UpdateBleV2MediumForScanning( + true, std::move(callback), *this); + return true; } -bool BleV2Medium::StopScanning() { return false; } +bool BleV2Medium::StopScanning() { + NEARBY_LOGS(INFO) << "G3 Ble StopScanning"; + absl::MutexLock lock(&mutex_); + + MediumEnvironment::Instance().UpdateBleV2MediumForScanning(false, {}, *this); + return true; +} std::unique_ptr BleV2Medium::StartGattServer( ServerGattConnectionCallback callback) { diff --git a/internal/platform/implementation/g3/bluetooth_adapter.cc b/internal/platform/implementation/g3/bluetooth_adapter.cc index bfa4b86b..83d5f209 100644 --- a/internal/platform/implementation/g3/bluetooth_adapter.cc +++ b/internal/platform/implementation/g3/bluetooth_adapter.cc @@ -16,9 +16,9 @@ #include +#include "internal/platform/implementation/g3/bluetooth_classic.h" #include "internal/platform/medium_environment.h" #include "internal/platform/prng.h" -#include "internal/platform/implementation/g3/bluetooth_classic.h" namespace location { namespace nearby { @@ -38,10 +38,16 @@ void BlePeripheral::SetAdvertisementBytes( advertisement_bytes_ = advertisement_bytes; } +BleV2Peripheral::BleV2Peripheral(BluetoothAdapter* adapter) + : adapter_(*adapter) {} + +std::string BleV2Peripheral::GetId() const { return adapter_.GetMacAddress(); } + BluetoothDevice::BluetoothDevice(BluetoothAdapter* adapter) : adapter_(*adapter) {} std::string BluetoothDevice::GetName() const { return adapter_.GetName(); } + std::string BluetoothDevice::GetMacAddress() const { return adapter_.GetMacAddress(); } diff --git a/internal/platform/implementation/g3/bluetooth_adapter.h b/internal/platform/implementation/g3/bluetooth_adapter.h index b1e80ed1..01a47a0c 100644 --- a/internal/platform/implementation/g3/bluetooth_adapter.h +++ b/internal/platform/implementation/g3/bluetooth_adapter.h @@ -21,6 +21,7 @@ #include "absl/strings/string_view.h" #include "absl/synchronization/mutex.h" #include "internal/platform/implementation/ble.h" +#include "internal/platform/implementation/ble_v2.h" #include "internal/platform/implementation/bluetooth_adapter.h" #include "internal/platform/implementation/bluetooth_classic.h" #include "internal/platform/implementation/g3/single_thread_executor.h" @@ -54,6 +55,21 @@ class BlePeripheral : public api::BlePeripheral { ByteArray advertisement_bytes_; }; +// BlePeripheral implementation. +class BleV2Peripheral : public api::ble_v2::BlePeripheral { + public: + std::string GetId() const override; + BluetoothAdapter& GetAdapter() { return adapter_; } + + private: + // Only BluetoothAdapter may instantiate BlePeripheral. + friend class BluetoothAdapter; + + explicit BleV2Peripheral(BluetoothAdapter* adapter); + + BluetoothAdapter& adapter_; +}; + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html. class BluetoothDevice : public api::BluetoothDevice { public: @@ -117,6 +133,7 @@ class BluetoothAdapter : public api::BluetoothAdapter { } BlePeripheral& GetPeripheral() { return peripheral_; } + BleV2Peripheral& GetPeripheralV2() { return peripheral_v2_; } void SetBleMedium(api::BleMedium* medium); api::BleMedium* GetBleMedium() { return ble_medium_; } @@ -127,6 +144,7 @@ class BluetoothAdapter : public api::BluetoothAdapter { mutable absl::Mutex mutex_; BluetoothDevice device_{this}; BlePeripheral peripheral_{this}; + BleV2Peripheral peripheral_v2_{this}; api::BluetoothClassicMedium* bluetooth_classic_medium_ = nullptr; api::BleMedium* ble_medium_ = nullptr; std::string mac_address_; diff --git a/internal/platform/medium_environment.cc b/internal/platform/medium_environment.cc index 79ae16c5..16331be8 100644 --- a/internal/platform/medium_environment.cc +++ b/internal/platform/medium_environment.cc @@ -22,10 +22,11 @@ #include #include +#include "internal/platform/count_down_latch.h" #include "internal/platform/feature_flags.h" +#include "internal/platform/implementation/ble_v2.h" #include "internal/platform/logging.h" #include "internal/platform/prng.h" -#include "internal/platform/count_down_latch.h" namespace location { namespace nearby { @@ -230,12 +231,21 @@ void MediumEnvironment::OnBlePeripheralStateChanged( } void MediumEnvironment::OnBleV2PeripheralStateChanged( - bool is_fast_advertisement, bool enabled, BleV2MediumContext& info) { + bool enabled, BleV2MediumContext& context, + const api::ble_v2::BleAdvertisementData& ble_advertisement_data, + api::ble_v2::BlePeripheral& peripheral) { if (!enabled_) return; - NEARBY_LOGS(INFO) << "G3 OnBleServiceStateChanged, context=" << &info - << ", notify=" << enable_notifications_.load(); + NEARBY_LOGS(INFO) << "G3 OnBleServiceStateChanged [peripheral impl=" + << &peripheral << "]; medium_context=" << &context + << "; notify=" << enable_notifications_.load(); if (!enable_notifications_) return; - // TODO(edwinwu): Report Advertisement found + NEARBY_LOGS(INFO) << "G3 [Run] OnBleServiceStateChanged [peripheral impl=" + << &peripheral << "]; context=" << &context + << "; notify=" << enabled; + if (enabled) { + context.scan_callback.advertisement_found_cb(peripheral, + ble_advertisement_data); + } } void MediumEnvironment::OnWifiLanServiceStateChanged( @@ -492,38 +502,88 @@ void MediumEnvironment::RegisterBleV2Medium(api::ble_v2::BleMedium& medium) { if (!enabled_) return; RunOnMediumEnvironmentThread([this, &medium]() { ble_v2_mediums_.insert({&medium, BleV2MediumContext{}}); - NEARBY_LOGS(INFO) << "Registered: medium:" << &medium; + NEARBY_LOGS(INFO) << "G3 Registered: medium:" << &medium; }); } -// TODO(b/213691253): Add g3 BleV2 medium tests after more functions are ready. void MediumEnvironment::UpdateBleV2MediumForAdvertising( - bool is_fast_advertisement, bool enabled, api::ble_v2::BleMedium& medium, - ByteArray* advertisement_byte) { + bool enabled, api::ble_v2::BleMedium& medium, + api::ble_v2::BlePeripheral& peripheral, + const api::ble_v2::BleAdvertisementData& advertisement_data) { if (!enabled_) return; - RunOnMediumEnvironmentThread([this, &medium, advertisement_byte, - is_fast_advertisement, enabled]() { - auto it = ble_v2_mediums_.find(&medium); - if (it == ble_v2_mediums_.end()) { - NEARBY_LOGS(INFO) << "UpdateBleMediumForAdvertising failed. There is no " - "medium registered."; - return; - } - auto& context = it->second; - context.advertisement_byte = advertisement_byte; - context.is_fast_advertisement = is_fast_advertisement; - NEARBY_LOGS(INFO) << "Update Ble medium for advertising: this=" << this - << ", medium=" << &medium - << ", is_fast_advertisement=" << is_fast_advertisement - << ", enabled=" << enabled; - for (auto& medium_info : ble_v2_mediums_) { - auto& local_medium = medium_info.first; - auto& info = medium_info.second; - // Do not send notification to the same medium. - if (local_medium == &medium) continue; - OnBleV2PeripheralStateChanged(is_fast_advertisement, enabled, info); - } - }); + RunOnMediumEnvironmentThread( + [this, &medium, &peripheral, advertisement_data = advertisement_data, + enabled]() { + auto it = ble_v2_mediums_.find(&medium); + if (it == ble_v2_mediums_.end()) { + NEARBY_LOGS(INFO) + << "G3 UpdateBleV2MediumForAdvertising failed. There is no " + "medium registered."; + return; + } + auto& context = it->second; + context.ble_peripheral = &peripheral; + context.advertising = enabled; + context.advertisement_data = advertisement_data; + NEARBY_LOGS(INFO) << "G3 UpdateBleV2MediumForAdvertising: this=" << this + << ", medium=" << &medium + << ", medium_context=" << &context + << ", peripheral=" << &peripheral + << ", enabled=" << enabled; + for (auto& medium_info : ble_v2_mediums_) { + const api::ble_v2::BleMedium* remote_medium = medium_info.first; + const BleV2MediumContext& remote_context = medium_info.second; + // Do not send notification to the same medium. + if (remote_medium == &medium) continue; + NEARBY_LOGS(INFO) + << "G3 UpdateBleV2MediumForAdvertising, found other medium=" + << remote_medium << ", remote_medium_context=" << &remote_context + << ", remote_context.peripheral=" << remote_context.ble_peripheral + << ". Ready to call OnBleV2PeripheralStateChanged."; + OnBleV2PeripheralStateChanged(enabled, context, + context.advertisement_data, + *context.ble_peripheral); + } + }); +} + +void MediumEnvironment::UpdateBleV2MediumForScanning( + bool enabled, BleScanCallback callback, api::ble_v2::BleMedium& medium) { + if (!enabled_) return; + RunOnMediumEnvironmentThread( + [this, &medium, callback = std::move(callback), enabled]() { + auto it = ble_v2_mediums_.find(&medium); + if (it == ble_v2_mediums_.end()) { + NEARBY_LOGS(INFO) + << "G3 UpdateBleV2MediumForScanning failed. There is no medium " + "registered."; + return; + } + BleV2MediumContext& context = it->second; + context.scan_callback = std::move(callback); + NEARBY_LOGS(INFO) << "G3 UpdateBleV2MediumForScanning: this=" << this + << ", medium=" << &medium + << ", medium_context=" << &context + << ", enabled=" << enabled; + if (enabled) { + for (const auto& medium_info : ble_v2_mediums_) { + const api::ble_v2::BleMedium* remote_medium = medium_info.first; + const BleV2MediumContext& remote_context = medium_info.second; + // Do not send notification to the same or the non-advertising + // medium. + if (remote_medium == &medium || !remote_context.advertising) + continue; + NEARBY_LOGS(INFO) + << "G3 UpdateBleV2MediumForScanning, found other medium=" + << remote_medium + << ", remote_medium_context=" << &remote_context + << ". Ready to call OnBleV2PeripheralStateChanged."; + OnBleV2PeripheralStateChanged(enabled, context, + remote_context.advertisement_data, + *remote_context.ble_peripheral); + } + } + }); } void MediumEnvironment::UnregisterBleV2Medium(api::ble_v2::BleMedium& medium) { @@ -531,7 +591,7 @@ void MediumEnvironment::UnregisterBleV2Medium(api::ble_v2::BleMedium& medium) { RunOnMediumEnvironmentThread([this, &medium]() { auto item = ble_v2_mediums_.extract(&medium); if (item.empty()) return; - NEARBY_LOGS(INFO) << "Unregistered Ble medium"; + NEARBY_LOGS(INFO) << "G3 Unregistered Ble medium"; }); } diff --git a/internal/platform/medium_environment.h b/internal/platform/medium_environment.h index e31ae5fd..c2c5bdf2 100644 --- a/internal/platform/medium_environment.h +++ b/internal/platform/medium_environment.h @@ -27,9 +27,9 @@ #ifndef NO_WEBRTC #include "internal/platform/implementation/webrtc.h" #endif -#include "internal/platform/implementation/wifi_lan.h" #include "internal/platform/byte_array.h" #include "internal/platform/feature_flags.h" +#include "internal/platform/implementation/wifi_lan.h" #include "internal/platform/listeners.h" #include "internal/platform/nsd_service_info.h" #include "internal/platform/single_thread_executor.h" @@ -59,6 +59,7 @@ class MediumEnvironment { api::BleMedium::DiscoveredPeripheralCallback; using BleAcceptedConnectionCallback = api::BleMedium::AcceptedConnectionCallback; + using BleScanCallback = api::ble_v2::BleMedium::ScanCallback; #ifndef NO_WEBRTC using OnSignalingMessageCallback = api::WebRtcSignalingMessenger::OnSignalingMessageCallback; @@ -116,15 +117,14 @@ class MediumEnvironment { void RegisterBluetoothMedium(api::BluetoothClassicMedium& medium, api::BluetoothAdapter& medium_adapter); - // Updates callback info to allow for dispatch of discovery events. + // Updates discovery callback info to allow for dispatch of discovery events. // // Invokes callback asynchronously when any changes happen to discoverable - // devices, or if the defice is turned off, whether or not it is discoverable, - // if it was ever reported as discoverable. + // devices if it is turned on. // // This should be called when discoverable state changes. - // with user-specified callback when discovery is enabled, and with default - // (empty) callback otherwise. + // A valid callback should be assigned when discovery `enabled` as true; or + // an empty callback is assigned with discovery `enabled` as false. void UpdateBluetoothMedium(api::BluetoothClassicMedium& medium, BluetoothDiscoveryCallback callback); @@ -179,12 +179,11 @@ class MediumEnvironment { // Updates discovery callback info to allow for dispatch of discovery events. // // Invokes callback asynchronously when any changes happen to discoverable - // devices, or if the defice is turned off, whether or not it is discoverable, - // if it was ever reported as discoverable. + // devices if it is turned on. // // This should be called when discoverable state changes. - // with user-specified callback when discovery is enabled, and with default - // (empty) callback otherwise. + // A valid callback should be assigned when discovery `enabled` as true; or + // an empty callback is assigned with discovery `enabled` as false. void UpdateBleMediumForScanning( api::BleMedium& medium, const std::string& service_id, const std::string& fast_advertisement_service_uuid, @@ -208,13 +207,27 @@ class MediumEnvironment { // Adds medium-related info to allow for scanning/advertising to work. // This provides acccess to this medium from other mediums, when protocol // expects they should communicate. + // The registered `medium` must refer to a valid instance that outlives this + // object. void RegisterBleV2Medium(api::ble_v2::BleMedium& medium); // Updates advertising info to indicate the current medium is exposing // advertising event. - void UpdateBleV2MediumForAdvertising(bool is_fast_advertisement, bool enabled, - api::ble_v2::BleMedium& medium, - ByteArray* advertisement_byte); + void UpdateBleV2MediumForAdvertising( + bool enabled, api::ble_v2::BleMedium& medium, + api::ble_v2::BlePeripheral& peripheral, + const api::ble_v2::BleAdvertisementData& advertisement_data); + + // Updates discovery callback info to allow for dispatch of discovery events. + // + // Invokes callback asynchronously when any changes happen to discoverable + // devices if it is turned on. + // + // 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, + api::ble_v2::BleMedium& medium); // Removes medium-related info. This should correspond to device power off. void UnregisterBleV2Medium(api::ble_v2::BleMedium& mediumum); @@ -233,8 +246,8 @@ class MediumEnvironment { // Updates discovery callback info to allow for dispatch of discovery events. // // This should be called when discoverable state changes. - // with user-specified callback when discovery is enabled, and with default - // (empty) callback otherwise. + // A valid callback should be assigned when discovery `enabled` as true; or + // an empty callback is assigned with discovery `enabled` as false. void UpdateWifiLanMediumForDiscovery( api::WifiLanMedium& medium, WifiLanDiscoveredServiceCallback callback, const std::string& service_type, bool enabled); @@ -271,8 +284,10 @@ class MediumEnvironment { }; struct BleV2MediumContext { - ByteArray* advertisement_byte; - bool is_fast_advertisement = false; + BleScanCallback scan_callback = {}; + api::ble_v2::BlePeripheral* ble_peripheral = nullptr; + api::ble_v2::BleAdvertisementData advertisement_data; + bool advertising = false; }; struct WifiLanMediumContext { @@ -303,8 +318,10 @@ class MediumEnvironment { const std::string& service_id, bool fast_advertisement, bool enabled); - void OnBleV2PeripheralStateChanged(bool is_fast_advertisement, bool enabled, - BleV2MediumContext& info); + void OnBleV2PeripheralStateChanged( + bool enabled, BleV2MediumContext& context, + const api::ble_v2::BleAdvertisementData& ble_advertisement_data, + api::ble_v2::BlePeripheral& peripheral); void OnWifiLanServiceStateChanged(WifiLanMediumContext& info, const NsdServiceInfo& service_info,