blob: 0aab94d3a26510b58198925db00014193c35859d [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/low_energy_advertising_manager.h"
#include <map>
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/advertising_data.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/assert.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/macros.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/connection.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/fake_local_address_delegate.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/fake_low_energy_connection.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/controller_test.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/fake_controller.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/emboss_control_packets.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/transport/error.h"
namespace bt {
using testing::FakeController;
namespace gap {
namespace {
namespace pwemb = pw::bluetooth::emboss;
using TestingBase = bt::testing::FakeDispatcherControllerTest<FakeController>;
constexpr size_t kDefaultMaxAdSize = 23;
constexpr size_t kDefaultFakeAdSize = 20;
constexpr AdvertisingInterval kTestInterval = AdvertisingInterval::FAST1;
const DeviceAddress kRandomAddress(DeviceAddress::Type::kLERandom,
{0x55, 0x44, 0x33, 0x22, 0x11, 0x00});
void NopConnectCallback(AdvertisementId, std::unique_ptr<hci::Connection>) {}
struct AdvertisementStatus {
AdvertisingData data;
AdvertisingData scan_rsp;
bool anonymous;
uint16_t interval_min;
uint16_t interval_max;
bool extended_pdu;
hci::LowEnergyAdvertiser::ConnectionCallback connect_cb;
};
// LowEnergyAdvertiser for testing purposes:
// - Reports mas_ad_size supported
// - Actually just accepts all ads and stores them in ad_store
class FakeLowEnergyAdvertiser final : public hci::LowEnergyAdvertiser {
public:
FakeLowEnergyAdvertiser(
const hci::Transport::WeakPtr& hci,
size_t max_ad_size,
std::unordered_map<DeviceAddress, AdvertisementStatus>* ad_store)
: hci::LowEnergyAdvertiser(hci),
max_ad_size_(max_ad_size),
ads_(ad_store),
hci_(hci) {
BT_ASSERT(ads_);
}
~FakeLowEnergyAdvertiser() override = default;
size_t GetSizeLimit(bool extended_pdu) const override { return max_ad_size_; }
size_t MaxAdvertisements() const override { return 1; }
bool AllowsRandomAddressChange() const override { return true; }
void StartAdvertising(const DeviceAddress& address,
const AdvertisingData& data,
const AdvertisingData& scan_rsp,
const AdvertisingOptions& options,
ConnectionCallback connect_callback,
hci::ResultFunction<> callback) override {
if (pending_error_.is_error()) {
callback(pending_error_);
pending_error_ = fit::ok();
return;
}
if (data.CalculateBlockSize(/*include_flags=*/true) > max_ad_size_) {
callback(ToResult(HostError::kInvalidParameters));
return;
}
if (scan_rsp.CalculateBlockSize(/*include_flags=*/false) > max_ad_size_) {
callback(ToResult(HostError::kInvalidParameters));
return;
}
AdvertisementStatus new_status;
data.Copy(&new_status.data);
scan_rsp.Copy(&new_status.scan_rsp);
new_status.connect_cb = std::move(connect_callback);
new_status.interval_min = options.interval.min();
new_status.interval_max = options.interval.max();
new_status.anonymous = options.anonymous;
new_status.extended_pdu = options.extended_pdu;
ads_->emplace(address, std::move(new_status));
callback(fit::ok());
}
void StopAdvertising(const DeviceAddress& address,
bool extended_pdu) override {
ads_->erase(address);
}
void OnIncomingConnection(
hci_spec::ConnectionHandle handle,
pwemb::ConnectionRole role,
const DeviceAddress& peer_address,
const hci_spec::LEConnectionParameters& conn_params) override {
// Right now, we call the first callback, because we can't call any other
// ones.
// TODO(jamuraa): make this send it to the correct callback once we can
// determine which one that is.
const auto& cb = ads_->begin()->second.connect_cb;
if (cb) {
cb(std::make_unique<hci::testing::FakeLowEnergyConnection>(
handle, ads_->begin()->first, peer_address, role, hci_));
}
}
// Sets this faker up to send an error back from the next StartAdvertising
// call. Set to success to disable a previously called error.
void ErrorOnNext(hci::Result<> error_status) {
pending_error_ = error_status;
}
private:
hci::EmbossCommandPacket BuildEnablePacket(
const DeviceAddress& address,
pw::bluetooth::emboss::GenericEnableParam enable,
bool extended_pdu) override {
return hci::EmbossCommandPacket::New<
pwemb::LESetExtendedAdvertisingEnableDataWriter>(
hci_spec::kLESetExtendedAdvertisingEnable);
}
std::optional<hci::EmbossCommandPacket> BuildSetAdvertisingParams(
const DeviceAddress& address,
pwemb::LEAdvertisingType type,
pwemb::LEOwnAddressType own_address_type,
const hci::AdvertisingIntervalRange& interval,
bool extended_pdu) override {
return std::nullopt;
}
std::vector<hci::EmbossCommandPacket> BuildSetAdvertisingData(
const DeviceAddress& address,
const AdvertisingData& data,
AdvFlags flags,
bool extended_pdu) override {
hci::EmbossCommandPacket packet =
hci::EmbossCommandPacket::New<pwemb::LESetAdvertisingDataCommandWriter>(
hci_spec::kLESetAdvertisingData);
std::vector<hci::EmbossCommandPacket> packets;
packets.push_back(std::move(packet));
return packets;
}
hci::EmbossCommandPacket BuildUnsetAdvertisingData(
const DeviceAddress& address, bool extended_pdu) override {
return hci::EmbossCommandPacket::New<
pwemb::LESetAdvertisingDataCommandWriter>(
hci_spec::kLESetAdvertisingData);
}
std::vector<hci::EmbossCommandPacket> BuildSetScanResponse(
const DeviceAddress& address,
const AdvertisingData& scan_rsp,
bool extended_pdu) override {
hci::EmbossCommandPacket packet = hci::EmbossCommandPacket::New<
pwemb::LESetScanResponseDataCommandWriter>(
hci_spec::kLESetScanResponseData);
std::vector<hci::EmbossCommandPacket> packets;
packets.push_back(std::move(packet));
return packets;
}
hci::EmbossCommandPacket BuildUnsetScanResponse(const DeviceAddress& address,
bool extended_pdu) override {
return hci::EmbossCommandPacket::New<
pwemb::LESetScanResponseDataCommandWriter>(
hci_spec::kLESetScanResponseData);
}
hci::EmbossCommandPacket BuildRemoveAdvertisingSet(
const DeviceAddress& address, bool extended_pdu) override {
return hci::EmbossCommandPacket::New<
pwemb::LERemoveAdvertisingSetCommandWriter>(
hci_spec::kLERemoveAdvertisingSet);
}
size_t max_ad_size_;
std::unordered_map<DeviceAddress, AdvertisementStatus>* ads_;
hci::Result<> pending_error_ = fit::ok();
hci::Transport::WeakPtr hci_;
BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(FakeLowEnergyAdvertiser);
};
class LowEnergyAdvertisingManagerTest : public TestingBase {
public:
LowEnergyAdvertisingManagerTest() = default;
~LowEnergyAdvertisingManagerTest() override = default;
protected:
void SetUp() override {
TestingBase::SetUp();
InitializeACLDataChannel();
fake_address_delegate_.set_local_address(kRandomAddress);
MakeFakeAdvertiser();
MakeAdvertisingManager();
}
void TearDown() override {
adv_mgr_ = nullptr;
advertiser_ = nullptr;
TestingBase::TearDown();
}
// Makes some fake advertising data of a specific |packed_size|
AdvertisingData CreateFakeAdvertisingData(
size_t packed_size = kDefaultFakeAdSize) {
AdvertisingData result;
StaticByteBuffer buffer(
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
size_t bytes_left = packed_size;
while (bytes_left > 0) {
// Each field to take 10 bytes total, unless the next header (4 bytes)
// won't fit. In which case we add enough bytes to finish up.
size_t data_bytes = bytes_left < 14 ? (bytes_left - 4) : 6;
EXPECT_TRUE(result.SetManufacturerData(0xb000 + bytes_left,
buffer.view(0, data_bytes)));
bytes_left = packed_size - result.CalculateBlockSize();
}
return result;
}
LowEnergyAdvertisingManager::AdvertisingStatusCallback GetErrorCallback() {
return [this](AdvertisementInstance instance, hci::Result<> status) {
EXPECT_EQ(kInvalidAdvertisementId, instance.id());
EXPECT_TRUE(status.is_error());
last_status_ = status;
};
}
LowEnergyAdvertisingManager::AdvertisingStatusCallback GetSuccessCallback() {
return [this](AdvertisementInstance instance, hci::Result<> status) {
EXPECT_NE(kInvalidAdvertisementId, instance.id());
EXPECT_EQ(fit::ok(), status);
last_instance_ = std::move(instance);
last_status_ = status;
};
}
void MakeFakeAdvertiser(size_t max_ad_size = kDefaultMaxAdSize) {
advertiser_ = std::make_unique<FakeLowEnergyAdvertiser>(
transport()->GetWeakPtr(), max_ad_size, &ad_store_);
}
void MakeAdvertisingManager() {
adv_mgr_ = std::make_unique<LowEnergyAdvertisingManager>(
advertiser(), &fake_address_delegate_);
}
LowEnergyAdvertisingManager* adv_mgr() const { return adv_mgr_.get(); }
const std::unordered_map<DeviceAddress, AdvertisementStatus>& ad_store() {
return ad_store_;
}
AdvertisementId last_ad_id() const { return last_instance_.id(); }
// Returns the currently active advertising state. This is useful for tests
// that want to verify advertising parameters when there is a single known
// advertisement. Returns nullptr if the number of advertisements are not
// equal to one.
const AdvertisementStatus* current_adv() const {
if (ad_store_.size() != 1u) {
return nullptr;
}
return &ad_store_.begin()->second;
}
// Returns and clears the last callback status. This resets the state to
// detect another callback.
std::optional<hci::Result<>> last_status() { return last_status_; }
FakeLowEnergyAdvertiser* advertiser() const { return advertiser_.get(); }
private:
hci::FakeLocalAddressDelegate fake_address_delegate_{dispatcher()};
// TODO(armansito): The address mapping is currently broken since the
// gap::LEAM always assigns the controller random address. Make this track
// each instance by instance ID instead once the layering issues have been
// fixed.
std::unordered_map<DeviceAddress, AdvertisementStatus> ad_store_;
AdvertisementInstance last_instance_;
std::optional<hci::Result<>> last_status_;
std::unique_ptr<FakeLowEnergyAdvertiser> advertiser_;
std::unique_ptr<LowEnergyAdvertisingManager> adv_mgr_;
BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(LowEnergyAdvertisingManagerTest);
};
// Tests:
// - When the advertiser succeeds, the callback is called with the success
TEST_F(LowEnergyAdvertisingManagerTest, Success) {
EXPECT_FALSE(adv_mgr()->advertising());
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
AdvertisingData(),
/*connect_callback=*/nullptr,
kTestInterval,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetSuccessCallback());
RunUntilIdle();
EXPECT_TRUE(last_status());
ASSERT_EQ(1u, ad_store().size());
EXPECT_TRUE(adv_mgr()->advertising());
// Verify that the advertiser uses the requested local address.
EXPECT_EQ(kRandomAddress, ad_store().begin()->first);
}
TEST_F(LowEnergyAdvertisingManagerTest, DataSize) {
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
AdvertisingData(),
/*connect_callback=*/nullptr,
kTestInterval,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetSuccessCallback());
RunUntilIdle();
EXPECT_TRUE(last_status());
EXPECT_EQ(1u, ad_store().size());
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(kDefaultMaxAdSize + 1),
AdvertisingData(),
/*connect_callback=*/nullptr,
kTestInterval,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetErrorCallback());
RunUntilIdle();
EXPECT_TRUE(last_status());
EXPECT_EQ(1u, ad_store().size());
}
// TODO(https://fxbug.dev/42083437): Revise this test to use multiple
// advertising instances when multi-advertising is supported.
// - Stopping one that is registered stops it in the advertiser
// (and stops the right address)
// - Stopping an advertisement that isn't registered returns false
TEST_F(LowEnergyAdvertisingManagerTest, RegisterUnregister) {
EXPECT_FALSE(adv_mgr()->StopAdvertising(kInvalidAdvertisementId));
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
AdvertisingData(),
/*connect_callback=*/nullptr,
kTestInterval,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetSuccessCallback());
RunUntilIdle();
EXPECT_TRUE(last_status());
EXPECT_EQ(1u, ad_store().size());
EXPECT_TRUE(adv_mgr()->advertising());
EXPECT_TRUE(adv_mgr()->StopAdvertising(last_ad_id()));
EXPECT_TRUE(ad_store().empty());
EXPECT_FALSE(adv_mgr()->advertising());
EXPECT_FALSE(adv_mgr()->StopAdvertising(last_ad_id()));
EXPECT_TRUE(ad_store().empty());
}
// - When the advertiser returns an error, we return an error
TEST_F(LowEnergyAdvertisingManagerTest, AdvertiserError) {
advertiser()->ErrorOnNext(
ToResult(pwemb::StatusCode::INVALID_HCI_COMMAND_PARAMETERS));
EXPECT_FALSE(adv_mgr()->advertising());
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
AdvertisingData(),
/*connect_callback=*/nullptr,
kTestInterval,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetErrorCallback());
RunUntilIdle();
EXPECT_TRUE(last_status());
EXPECT_FALSE(adv_mgr()->advertising());
}
// - It calls the connectable callback correctly when connected to
TEST_F(LowEnergyAdvertisingManagerTest, ConnectCallback) {
std::unique_ptr<hci::LowEnergyConnection> link;
AdvertisementId advertised_id = kInvalidAdvertisementId;
auto connect_cb = [&](AdvertisementId connected_id,
std::unique_ptr<hci::LowEnergyConnection> cb_link) {
link = std::move(cb_link);
EXPECT_EQ(advertised_id, connected_id);
};
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
AdvertisingData(),
connect_cb,
kTestInterval,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetSuccessCallback());
RunUntilIdle();
EXPECT_TRUE(last_status());
advertised_id = last_ad_id();
DeviceAddress peer_address(DeviceAddress::Type::kLEPublic,
{3, 2, 1, 1, 2, 3});
advertiser()->OnIncomingConnection(1,
pwemb::ConnectionRole::PERIPHERAL,
peer_address,
hci_spec::LEConnectionParameters());
RunUntilIdle();
ASSERT_TRUE(link);
// Make sure that the link has the correct local and peer addresses
// assigned.
EXPECT_EQ(kRandomAddress, link->local_address());
EXPECT_EQ(peer_address, link->peer_address());
}
// - Error: Connectable and Anonymous at the same time
TEST_F(LowEnergyAdvertisingManagerTest, ConnectAdvertiseError) {
auto connect_cb = [](AdvertisementId connected_id,
std::unique_ptr<hci::LowEnergyConnection> conn) {};
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
AdvertisingData(),
connect_cb,
kTestInterval,
/*extended_pdu=*/false,
/*anonymous=*/true,
/*include_tx_power_level=*/false,
GetErrorCallback());
EXPECT_TRUE(last_status());
}
// Passes the values for the data on. (anonymous, data, scan_rsp)
TEST_F(LowEnergyAdvertisingManagerTest, SendsCorrectData) {
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
CreateFakeAdvertisingData(/*packed_size=*/21),
/*connect_callback=*/nullptr,
kTestInterval,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetSuccessCallback());
RunUntilIdle();
EXPECT_TRUE(last_status());
EXPECT_EQ(1u, ad_store().size());
auto ad_status = &ad_store().begin()->second;
AdvertisingData expected_ad = CreateFakeAdvertisingData();
AdvertisingData expected_scan_rsp =
CreateFakeAdvertisingData(/*packed_size=*/21);
EXPECT_EQ(expected_ad, ad_status->data);
EXPECT_EQ(expected_scan_rsp, ad_status->scan_rsp);
EXPECT_EQ(false, ad_status->anonymous);
EXPECT_EQ(nullptr, ad_status->connect_cb);
}
// Test that the AdvertisingInterval values map to the spec defined constants
// (NOTE: this might change in the future in favor of a more advanced policy
// for managing the intervals; for now they get mapped to recommended values
// from Vol 3, Part C, Appendix A).
TEST_F(LowEnergyAdvertisingManagerTest, ConnectableAdvertisingIntervals) {
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
CreateFakeAdvertisingData(/*packed_size=*/21),
NopConnectCallback,
AdvertisingInterval::FAST1,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetSuccessCallback());
RunUntilIdle();
ASSERT_TRUE(last_status());
ASSERT_TRUE(current_adv());
EXPECT_EQ(kLEAdvertisingFastIntervalMin1, current_adv()->interval_min);
EXPECT_EQ(kLEAdvertisingFastIntervalMax1, current_adv()->interval_max);
ASSERT_TRUE(adv_mgr()->StopAdvertising(last_ad_id()));
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
CreateFakeAdvertisingData(/*packed_size=*/21),
NopConnectCallback,
AdvertisingInterval::FAST2,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetSuccessCallback());
RunUntilIdle();
ASSERT_TRUE(last_status());
ASSERT_TRUE(current_adv());
EXPECT_EQ(kLEAdvertisingFastIntervalMin2, current_adv()->interval_min);
EXPECT_EQ(kLEAdvertisingFastIntervalMax2, current_adv()->interval_max);
ASSERT_TRUE(adv_mgr()->StopAdvertising(last_ad_id()));
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
CreateFakeAdvertisingData(/*packed_size=*/21),
NopConnectCallback,
AdvertisingInterval::SLOW,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetSuccessCallback());
RunUntilIdle();
ASSERT_TRUE(last_status());
ASSERT_TRUE(current_adv());
EXPECT_EQ(kLEAdvertisingSlowIntervalMin, current_adv()->interval_min);
EXPECT_EQ(kLEAdvertisingSlowIntervalMax, current_adv()->interval_max);
ASSERT_TRUE(adv_mgr()->StopAdvertising(last_ad_id()));
}
TEST_F(LowEnergyAdvertisingManagerTest, NonConnectableAdvertisingIntervals) {
AdvertisingData fake_ad = CreateFakeAdvertisingData();
AdvertisingData scan_rsp = CreateFakeAdvertisingData(21 /* size of ad */);
// We expect FAST1 to fall back to FAST2 due to specification recommendation
// (Vol 3, Part C, Appendix A) and lack of support for non-connectable
// advertising with FAST1 parameters on certain controllers.
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
CreateFakeAdvertisingData(/*packed_size=*/21),
/*connect_callback=*/nullptr,
AdvertisingInterval::FAST1,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetSuccessCallback());
RunUntilIdle();
ASSERT_TRUE(last_status());
ASSERT_TRUE(current_adv());
EXPECT_EQ(kLEAdvertisingFastIntervalMin2, current_adv()->interval_min);
EXPECT_EQ(kLEAdvertisingFastIntervalMax2, current_adv()->interval_max);
ASSERT_TRUE(adv_mgr()->StopAdvertising(last_ad_id()));
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
CreateFakeAdvertisingData(/*packed_size=*/21),
/*connect_callback=*/nullptr,
AdvertisingInterval::FAST2,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetSuccessCallback());
RunUntilIdle();
ASSERT_TRUE(last_status());
ASSERT_TRUE(current_adv());
EXPECT_EQ(kLEAdvertisingFastIntervalMin2, current_adv()->interval_min);
EXPECT_EQ(kLEAdvertisingFastIntervalMax2, current_adv()->interval_max);
ASSERT_TRUE(adv_mgr()->StopAdvertising(last_ad_id()));
adv_mgr()->StartAdvertising(CreateFakeAdvertisingData(),
CreateFakeAdvertisingData(/*packed_size=*/21),
/*connect_callback=*/nullptr,
AdvertisingInterval::SLOW,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
GetSuccessCallback());
RunUntilIdle();
ASSERT_TRUE(last_status());
ASSERT_TRUE(current_adv());
EXPECT_EQ(kLEAdvertisingSlowIntervalMin, current_adv()->interval_min);
EXPECT_EQ(kLEAdvertisingSlowIntervalMax, current_adv()->interval_max);
ASSERT_TRUE(adv_mgr()->StopAdvertising(last_ad_id()));
}
TEST_F(LowEnergyAdvertisingManagerTest, DestroyingInstanceStopsAdvertisement) {
{
AdvertisementInstance instance;
adv_mgr()->StartAdvertising(AdvertisingData(),
AdvertisingData(),
/*connect_callback=*/nullptr,
AdvertisingInterval::FAST1,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
[&](AdvertisementInstance i, auto status) {
ASSERT_EQ(fit::ok(), status);
instance = std::move(i);
});
RunUntilIdle();
EXPECT_TRUE(adv_mgr()->advertising());
// Destroying |instance| should stop the advertisement.
}
RunUntilIdle();
EXPECT_FALSE(adv_mgr()->advertising());
}
TEST_F(LowEnergyAdvertisingManagerTest, MovingIntoInstanceStopsAdvertisement) {
AdvertisementInstance instance;
adv_mgr()->StartAdvertising(AdvertisingData(),
AdvertisingData(),
/*connect_callback=*/nullptr,
AdvertisingInterval::FAST1,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
[&](AdvertisementInstance i, auto status) {
ASSERT_EQ(fit::ok(), status);
instance = std::move(i);
});
RunUntilIdle();
EXPECT_TRUE(adv_mgr()->advertising());
// Destroying |instance| by invoking the move assignment operator should
// stop the advertisement.
instance = {};
RunUntilIdle();
EXPECT_FALSE(adv_mgr()->advertising());
}
TEST_F(LowEnergyAdvertisingManagerTest,
MovingInstanceTransfersOwnershipOfAdvertisement) {
auto instance = std::make_unique<AdvertisementInstance>();
adv_mgr()->StartAdvertising(AdvertisingData(),
AdvertisingData(),
/*connect_callback=*/nullptr,
AdvertisingInterval::FAST1,
/*extended_pdu=*/false,
/*anonymous=*/false,
/*include_tx_power_level=*/false,
[&](AdvertisementInstance i, auto status) {
ASSERT_EQ(fit::ok(), status);
*instance = std::move(i);
});
RunUntilIdle();
EXPECT_TRUE(adv_mgr()->advertising());
// Moving |instance| should transfer the ownership of the advertisement
// (assignment).
{
AdvertisementInstance move_assigned_instance = std::move(*instance);
// Explicitly clearing the old instance should have no effect.
*instance = {};
RunUntilIdle();
EXPECT_TRUE(adv_mgr()->advertising());
*instance = std::move(move_assigned_instance);
}
// Advertisement should not stop when |move_assigned_instance| goes out of
// scope as it no longer owns the advertisement.
RunUntilIdle();
EXPECT_TRUE(adv_mgr()->advertising());
// Moving |instance| should transfer the ownership of the advertisement
// (move-constructor).
{
AdvertisementInstance move_constructed_instance(std::move(*instance));
// Explicitly destroying the old instance should have no effect.
instance.reset();
RunUntilIdle();
EXPECT_TRUE(adv_mgr()->advertising());
}
// Advertisement should stop when |move_constructed_instance| goes out of
// scope.
RunUntilIdle();
EXPECT_FALSE(adv_mgr()->advertising());
}
} // namespace
} // namespace gap
} // namespace bt