blob: f66b4411956fe1a76c06168c8016e5d2340a3835 [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 "garnet/drivers/bluetooth/lib/common/log.h"
#include "garnet/drivers/bluetooth/lib/common/slab_allocator.h"
#include "garnet/drivers/bluetooth/lib/gap/random_address_generator.h"
#include "garnet/drivers/bluetooth/lib/gap/remote_device.h"
#include "garnet/drivers/bluetooth/lib/hci/util.h"
#include "lib/fxl/random/uuid.h"
#include "lib/fxl/strings/string_printf.h"
namespace btlib {
namespace gap {
namespace {
constexpr size_t kFlagsSize = 3;
constexpr uint8_t kDefaultFlags = 0;
// Write the block for the flags to the |buffer|.
void WriteFlags(common::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:
explicit ActiveAdvertisement(const common::DeviceAddress& address)
: address_(address), id_(fxl::GenerateUUID()) {}
~ActiveAdvertisement() = default;
const common::DeviceAddress& address() const { return address_; }
const std::string& id() const { return id_; }
private:
common::DeviceAddress address_;
std::string id_;
FXL_DISALLOW_COPY_AND_ASSIGN(ActiveAdvertisement);
};
LowEnergyAdvertisingManager::LowEnergyAdvertisingManager(
hci::LowEnergyAdvertiser* advertiser)
: advertiser_(advertiser), weak_ptr_factory_(this) {
ZX_DEBUG_ASSERT(advertiser_);
}
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,
uint32_t interval_ms,
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("", hci::Status(common::HostError::kInvalidParameters));
return;
}
// Generate the DeviceAddress and id
// TODO(armansito): Extract private address generation to a centrally
// accessible location and persist the address so that it can be used for
// pairing.
auto address = RandomAddressGenerator::PrivateAddress();
auto ad_ptr = std::make_unique<ActiveAdvertisement>(address);
auto self = weak_ptr_factory_.GetWeakPtr();
hci::LowEnergyAdvertiser::ConnectionCallback adv_conn_cb;
if (connect_callback) {
adv_conn_cb = [self, id = ad_ptr->id(),
connect_callback = std::move(connect_callback)](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_callback(id, std::move(link));
};
}
auto status_cb = [self, ad_ptr = std::move(ad_ptr),
status_callback = std::move(status_callback)](
uint32_t, hci::Status status) mutable {
if (!self)
return;
if (!status) {
status_callback("", status);
return;
}
const std::string& id = ad_ptr->id();
self->advertisements_.emplace(id, std::move(ad_ptr));
status_callback(id, status);
};
// Serialize the data
auto data_block =
common::NewSlabBuffer(data.CalculateBlockSize() + kFlagsSize);
WriteFlags(data_block.get());
auto data_view = data_block->mutable_view(kFlagsSize);
data.WriteBlock(&data_view);
auto scan_rsp_block = common::NewSlabBuffer(scan_rsp.CalculateBlockSize());
scan_rsp.WriteBlock(scan_rsp_block.get());
// Call StartAdvertising, with the callback
advertiser_->StartAdvertising(address, *data_block, *scan_rsp_block,
std::move(adv_conn_cb), interval_ms, anonymous,
std::move(status_cb));
}
bool LowEnergyAdvertisingManager::StopAdvertising(
std::string 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 btlib