blob: 81be76714c97e8a98357788158f5e5e3bd1f071f [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 "low_energy_advertising_manager.h"
#include <zircon/assert.h>
#include "low_energy_address_manager.h"
#include "peer.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/common/random.h"
#include "src/connectivity/bluetooth/core/bt-host/common/slab_allocator.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/util.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace bt {
namespace gap {
namespace {
constexpr size_t kFlagsSize = 3;
constexpr uint8_t kDefaultFlags = 0;
// Write the block for the flags to the |buffer|.
void WriteFlags(MutableByteBuffer* buffer, bool limited = false) {
ZX_DEBUG_ASSERT(buffer->size() >= kFlagsSize);
(*buffer)[0] = 2;
(*buffer)[1] = static_cast<uint8_t>(DataType::kFlags);
if (limited) {
(*buffer)[2] = kDefaultFlags | AdvFlag::kLELimitedDiscoverableMode;
} else {
(*buffer)[2] = kDefaultFlags | AdvFlag::kLEGeneralDiscoverableMode;
}
}
} // namespace
class LowEnergyAdvertisingManager::ActiveAdvertisement final {
public:
// TODO(BT-270): Don't randomly generate the ID of an advertisement.
// Instead use a counter like other internal IDs once this ID is not visible
// outside of bt-host.
explicit ActiveAdvertisement(const DeviceAddress& address)
: address_(address), id_(RandomPeerId().value()) {}
~ActiveAdvertisement() = default;
const DeviceAddress& address() const { return address_; }
AdvertisementId id() const { return id_; }
private:
DeviceAddress address_;
AdvertisementId id_;
DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(ActiveAdvertisement);
};
LowEnergyAdvertisingManager::LowEnergyAdvertisingManager(
hci::LowEnergyAdvertiser* advertiser,
hci::LocalAddressDelegate* local_addr_delegate)
: advertiser_(advertiser),
local_addr_delegate_(local_addr_delegate),
weak_ptr_factory_(this) {
ZX_DEBUG_ASSERT(advertiser_);
ZX_DEBUG_ASSERT(local_addr_delegate_);
}
LowEnergyAdvertisingManager::~LowEnergyAdvertisingManager() {
// Turn off all the advertisements!
for (const auto& ad : advertisements_) {
advertiser_->StopAdvertising(ad.second->address());
}
}
void LowEnergyAdvertisingManager::StartAdvertising(
const AdvertisingData& data, const AdvertisingData& scan_rsp,
ConnectionCallback connect_callback, zx::duration interval, bool anonymous,
AdvertisingStatusCallback status_callback) {
// Can't be anonymous and connectable
if (anonymous && connect_callback) {
bt_log(TRACE, "gap-le", "can't advertise anonymously and connectable!");
status_callback(kInvalidAdvertisementId,
hci::Status(HostError::kInvalidParameters));
return;
}
// Serialize the data
auto data_bytes = NewSlabBuffer(data.CalculateBlockSize() + kFlagsSize);
WriteFlags(data_bytes.get());
auto data_view = data_bytes->mutable_view(kFlagsSize);
data.WriteBlock(&data_view);
auto scan_rsp_bytes = NewSlabBuffer(scan_rsp.CalculateBlockSize());
scan_rsp.WriteBlock(scan_rsp_bytes.get());
auto self = weak_ptr_factory_.GetWeakPtr();
// TODO(BT-742): The address used for legacy advertising must be
// coordinated via |local_addr_delegate_| however a unique address can be
// generated and assigned to each advertising set when the controller
// supports 5.0 extended advertising. hci::LowEnergyAdvertiser needs to be
// revised to not use device addresses to distinguish between advertising
// instances especially since |local_addr_delegate_| is likely to return the
// same address if called frequently.
//
// Revisit this logic when multi-advertising is supported.
local_addr_delegate_->EnsureLocalAddress(
[self, interval, anonymous, data_bytes = std::move(data_bytes),
scan_rsp_bytes = std::move(scan_rsp_bytes),
connect_cb = std::move(connect_callback),
status_cb = std::move(status_callback)](const auto& address) mutable {
if (!self)
return;
auto ad_ptr = std::make_unique<ActiveAdvertisement>(address);
hci::LowEnergyAdvertiser::ConnectionCallback adv_conn_cb;
if (connect_cb) {
adv_conn_cb = [self, id = ad_ptr->id(),
connect_cb = std::move(connect_cb)](auto link) {
bt_log(TRACE, "gap-le", "received new connection");
if (!self)
return;
// remove the advertiser because advertising has stopped
self->advertisements_.erase(id);
connect_cb(id, std::move(link));
};
}
auto status_cb_wrapper = [self, ad_ptr = std::move(ad_ptr),
status_cb = std::move(status_cb)](
zx::duration, hci::Status status) mutable {
if (!self)
return;
if (!status) {
status_cb(kInvalidAdvertisementId, status);
return;
}
auto id = ad_ptr->id();
self->advertisements_.emplace(id, std::move(ad_ptr));
status_cb(id, status);
};
// Call StartAdvertising, with the callback
self->advertiser_->StartAdvertising(
address, *data_bytes, *scan_rsp_bytes, std::move(adv_conn_cb),
interval, anonymous, std::move(status_cb_wrapper));
});
}
bool LowEnergyAdvertisingManager::StopAdvertising(
AdvertisementId advertisement_id) {
auto it = advertisements_.find(advertisement_id);
if (it == advertisements_.end())
return false;
advertiser_->StopAdvertising(it->second->address());
advertisements_.erase(it);
return true;
}
} // namespace gap
} // namespace bt