Add StartAdvertising to BLE medium.

StartAdvertising allows us to broadcast Nearby Presence advertisements.
We can now test scanning and advertising with the MediumEnvironment rather than mocking platform code.

PiperOrigin-RevId: 484662211
This commit is contained in:
Janusz Sobczak
2022-10-28 18:23:30 -07:00
committed by Copybara-Service
parent 5d742558a4
commit 81f6b547bb
5 changed files with 107 additions and 51 deletions
@@ -104,6 +104,7 @@ cc_library(
"@boringssl//:crypto",
"@com_google_absl//absl/strings",
],
alwayslink = 1,
)
cc_library(
@@ -262,10 +262,17 @@ std::unique_ptr<BleV2Medium::AdvertisingSession> BleV2Medium::StartAdvertising(
return nullptr;
}
if (callback.start_advertising_result) {
callback.start_advertising_result(BleOperationStatus::kSucceeded);
}
absl::MutexLock lock(&mutex_);
MediumEnvironment::Instance().UpdateBleV2MediumForAdvertising(
/*enabled=*/true, *this, adapter_->GetPeripheralV2(), advertising_data);
return std::make_unique<AdvertisingSession>(AdvertisingSession{});
return std::make_unique<AdvertisingSession>(
AdvertisingSession{.stop_advertising = [this] {
return StopAdvertising() ? BleOperationStatus::kSucceeded
: BleOperationStatus::kFailed;
}});
}
bool BleV2Medium::StartScanning(const Uuid& service_uuid,
+45 -19
View File
@@ -16,9 +16,10 @@
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_BLE_H_
#include <memory>
#include <string>
#include "internal/platform/ble_v2.h"
#include "internal/platform/bluetooth_adapter.h"
#include "internal/platform/implementation/ble_v2.h"
#include "internal/platform/uuid.h"
#include "presence/power_mode.h"
#include "presence/scan_request.h"
@@ -30,42 +31,65 @@ namespace presence {
ABSL_CONST_INIT const location::nearby::Uuid kPresenceServiceUuid(
0x0000fcf100001000, 0x800000805f9b34fb);
using ScanningSession =
::location::nearby::api::ble_v2::BleMedium::ScanningSession;
using ScanningCallback =
::location::nearby::api::ble_v2::BleMedium::ScanningCallback;
using ::location::nearby::api::ble_v2::TxPowerLevel;
/*
* This Ble class utilizes platform/ble_v2 BleV2Medium, provides ble functions
* for presence logic layer to invoke.
* This class would have states like if ble is available or not, if it's doing
* broadcast/scan.
*/
template <typename Medium>
// since we are using template for test, then we need to keep functions defs in
// the header, more details: go/cstyle#Self_contained_Headers.
class Ble {
public:
using TxPowerLevel = ::location::nearby::api::ble_v2::TxPowerLevel;
using ScanningSession =
::location::nearby::api::ble_v2::BleMedium::ScanningSession;
using ScanningCallback =
::location::nearby::api::ble_v2::BleMedium::ScanningCallback;
using AdvertiseParameters =
::location::nearby::api::ble_v2::AdvertiseParameters;
using AdvertisingSession =
::location::nearby::api::ble_v2::BleMedium::AdvertisingSession;
using AdvertisingCallback =
::location::nearby::api::ble_v2::BleMedium::AdvertisingCallback;
using BleAdvertisementData =
::location::nearby::api::ble_v2::BleAdvertisementData;
using BleMedium = ::location::nearby::api::ble_v2::BleMedium;
explicit Ble(location::nearby::BluetoothAdapter& bluetooth_adapter)
: adapter_(bluetooth_adapter),
medium_(std::make_unique<Medium>(bluetooth_adapter)) {}
~Ble() = default;
: medium_(bluetooth_adapter) {}
bool IsAvailable() const { return medium_->IsValid(); }
bool IsAvailable() const { return medium_.IsValid(); }
// Starts broadcasting NP advertisement in `payload`. The caller should use
// the returned `AdvertisingSession` to stop the broadcast.
std::unique_ptr<AdvertisingSession> StartAdvertising(
absl::string_view payload, bool is_extended_advertisement,
PowerMode power_mode, AdvertisingCallback callback) {
BleAdvertisementData advertising_data = {.is_extended_advertisement =
is_extended_advertisement};
advertising_data.service_data.insert(
{kPresenceServiceUuid,
location::nearby::ByteArray(std::string(payload))});
AdvertiseParameters advertise_set_parameters = {
.tx_power_level = ConvertPowerModeToPowerLevel(power_mode),
.is_connectable = true,
};
return medium_.StartAdvertising(advertising_data, advertise_set_parameters,
callback);
}
// Starts scanning for NP advertisements. The caller should use the returned
// `ScanningSession` to stop scanning.
std::unique_ptr<ScanningSession> StartScanning(ScanRequest scan_request,
ScanningCallback callback) {
return medium_->StartScanning(
return medium_.StartScanning(
kPresenceServiceUuid,
ConvertPowerModeToPowerLevel(scan_request.power_mode), callback);
}
private:
friend class BleTest;
location::nearby::BluetoothAdapter& adapter_;
std::unique_ptr<Medium> medium_;
// Provides access to platform implementation. It's used in tests.
BleMedium* GetImpl() const { return medium_.GetImpl(); }
private:
TxPowerLevel ConvertPowerModeToPowerLevel(PowerMode power_mode) {
switch (power_mode) {
case PowerMode::kNoPower:
@@ -79,6 +103,8 @@ class Ble {
}
return TxPowerLevel::kUnknown;
}
location::nearby::BleV2Medium medium_;
};
} // namespace presence
+51 -25
View File
@@ -32,6 +32,7 @@
namespace nearby {
namespace presence {
namespace {
using FeatureFlags = location::nearby::FeatureFlags::Flags;
using BleOperationStatus = location::nearby::api::ble_v2::BleOperationStatus;
@@ -43,6 +44,12 @@ using TxPowerLevel = location::nearby::api::ble_v2::TxPowerLevel;
using ScanningCallback =
location::nearby::api::ble_v2::BleMedium::ScanningCallback;
using Uuid = location::nearby::Uuid;
using location::nearby::api::ble_v2::BleAdvertisementData;
using location::nearby::api::ble_v2::BlePeripheral;
using AdvertisingCallback =
location::nearby::api::ble_v2::BleMedium::AdvertisingCallback;
using AdvertisingSession =
location::nearby::api::ble_v2::BleMedium::AdvertisingSession;
constexpr FeatureFlags kTestCases[] = {
FeatureFlags{},
@@ -50,14 +57,6 @@ constexpr FeatureFlags kTestCases[] = {
class BleTest : public testing::TestWithParam<FeatureFlags> {
public:
class MockBleMedium {
public:
explicit MockBleMedium(location::nearby::BluetoothAdapter& adapter){}
MOCK_METHOD((std::unique_ptr<ScanningSession>), StartScanning,
(const Uuid& service_uuid, TxPowerLevel tx_power_level,
ScanningCallback callback));
};
constexpr static absl::Duration kWaitDuration = absl::Milliseconds(1000);
std::string account_name_ = "Test-Name";
@@ -88,12 +87,8 @@ class BleTest : public testing::TestWithParam<FeatureFlags> {
protected:
BleTest() { env_.Stop(); }
absl::optional<BleV2MediumStatus> GetBleStatus(
const Ble<location::nearby::BleV2Medium>& ble) {
return env_.GetBleV2MediumStatus(*ble.medium_->GetImpl());
}
MockBleMedium* GetMedium(const Ble<MockBleMedium>& ble) {
return ble.medium_.get();
absl::optional<BleV2MediumStatus> GetBleStatus(const Ble& ble) {
return env_.GetBleV2MediumStatus(*ble.GetImpl());
}
location::nearby::MediumEnvironment& env_{
location::nearby::MediumEnvironment::Instance()};
@@ -107,7 +102,7 @@ INSTANTIATE_TEST_SUITE_P(ParametrisedBleTest, BleTest,
TEST_P(BleTest, CanStartThenStopScanning) {
env_.Start();
::location::nearby::BluetoothAdapter adapter;
Ble<location::nearby::BleV2Medium> ble(adapter);
Ble ble(adapter);
ScanRequest scan_request{
.power_mode = PowerMode::kBalanced,
@@ -136,20 +131,51 @@ TEST_P(BleTest, CanStartThenStopScanning) {
env_.Stop();
}
// Using MockBleMedium to verify StartScanning is using the expected parameters
// with underneath BleMedium.
TEST_P(BleTest, VerifyStartScanning) {
TEST_P(BleTest, AdvertiseAndScan) {
// Create two Ble devices, one advertises, the other one scans, and verify
// that the NP advertisement was sent from one to the other.
env_.Start();
::location::nearby::BluetoothAdapter adapter;
Ble<MockBleMedium> ble(adapter);
EXPECT_CALL(*GetMedium(ble), StartScanning(kPresenceServiceUuid,
TxPowerLevel::kMedium, testing::_))
.Times(1);
location::nearby::BluetoothAdapter client_adapter;
Ble client(client_adapter);
location::nearby::BluetoothAdapter server_adapter;
Ble server(server_adapter);
std::string advert_data = "my advertisement";
ScanRequest scan_request{
.power_mode = PowerMode::kBalanced,
};
location::nearby::CountDownLatch advertise_latch(1);
location::nearby::CountDownLatch scan_latch(1);
std::vector<BleAdvertisementData> advertisements;
std::unique_ptr<ScanningSession> scanning_session = client.StartScanning(
scan_request,
ScanningCallback{.advertisement_found_cb =
[&](BlePeripheral& peripheral,
BleAdvertisementData advertisement_data) {
advertisements.push_back(advertisement_data);
scan_latch.CountDown();
}});
std::unique_ptr<location::nearby::api::ble_v2::BleMedium::AdvertisingSession>
advertising_session = server.StartAdvertising(
advert_data, /*is_extended_advertisement=*/false,
PowerMode::kBalanced,
AdvertisingCallback{
.start_advertising_result = [&](BleOperationStatus status) {
advertise_latch.CountDown();
}});
std::unique_ptr<ScanningSession> scanning_session =
ble.StartScanning(scan_request_, ScanningCallback{});
EXPECT_TRUE(advertise_latch.Await(kWaitDuration).result());
EXPECT_TRUE(scan_latch.Await(kWaitDuration).result());
EXPECT_EQ(scanning_session->stop_scanning(), BleOperationStatus::kSucceeded);
EXPECT_EQ(advertising_session->stop_advertising(),
BleOperationStatus::kSucceeded);
ASSERT_FALSE(advertisements.empty());
EXPECT_EQ(advertisements[0]
.service_data.find(kPresenceServiceUuid)
->second.AsStringView(),
advert_data);
env_.Stop();
}
} // namespace
} // namespace presence
} // namespace nearby
+2 -6
View File
@@ -15,7 +15,6 @@
#ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_MEDIUMS_H_
#define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_MEDIUMS_MEDIUMS_H_
#include "internal/platform/ble_v2.h"
#include "internal/platform/bluetooth_adapter.h"
#include "presence/implementation/mediums/ble.h"
@@ -28,15 +27,12 @@ namespace presence {
*/
class Mediums {
public:
Mediums() = default;
~Mediums() = default;
// Returns a handle to the Ble medium.
Ble<location::nearby::BleV2Medium>& GetBle();
Ble& GetBle();
private:
location::nearby::BluetoothAdapter adapter_;
Ble<location::nearby::BleV2Medium> ble_{adapter_};
Ble ble_{adapter_};
};
} // namespace presence