blob: 99b8cb5ad3c2e314b9136e6a4acdd075d17b2aa7 [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2017 The Android Open Source Project
* Copyright (C) 2014 Broadcom Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#include <base/bind.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/time/time.h>
#include <string.h>
#include <queue>
#include <vector>
#include "bt_target.h"
#include "device/include/controller.h"
#include "osi/include/alarm.h"
#include "ble_advertiser.h"
#include "ble_advertiser_hci_interface.h"
#include "btm_int_types.h"
using base::Bind;
using base::TimeDelta;
using base::TimeTicks;
using RegisterCb =
base::Callback<void(uint8_t /* inst_id */, uint8_t /* status */)>;
using IdTxPowerStatusCb = base::Callback<void(
uint8_t /* inst_id */, int8_t /* tx_power */, uint8_t /* status */)>;
using SetEnableData = BleAdvertiserHciInterface::SetEnableData;
extern void btm_gen_resolvable_private_addr(
base::Callback<void(uint8_t[8])> cb);
constexpr int ADV_DATA_LEN_MAX = 251;
namespace {
bool is_connectable(uint16_t advertising_event_properties) {
return advertising_event_properties & 0x01;
}
struct AdvertisingInstance {
uint8_t inst_id;
bool in_use;
uint8_t advertising_event_properties;
alarm_t* adv_raddr_timer;
int8_t tx_power;
uint16_t duration; // 1 unit is 10ms
uint8_t maxExtAdvEvents;
alarm_t* timeout_timer;
uint8_t own_address_type;
RawAddress own_address;
MultiAdvCb timeout_cb;
bool address_update_required;
bool periodic_enabled;
uint32_t advertising_interval; // 1 unit is 0.625 ms
/* When true, advertising set is enabled, or last scheduled call to "LE Set
* Extended Advertising Set Enable" is to enable this advertising set. Any
* command scheduled when in this state will execute when the set is enabled,
* unless enabling fails.
*
* When false, advertising set is disabled, or last scheduled call to "LE Set
* Extended Advertising Set Enable" is to disable this advertising set. Any
* command scheduled when in this state will execute when the set is disabled.
*/
bool enable_status;
TimeTicks enable_time;
bool IsEnabled() { return enable_status; }
bool IsConnectable() { return is_connectable(advertising_event_properties); }
AdvertisingInstance(int inst_id)
: inst_id(inst_id),
in_use(false),
advertising_event_properties(0),
tx_power(0),
duration(0),
timeout_timer(nullptr),
own_address_type(0),
own_address(RawAddress::kEmpty),
address_update_required(false),
periodic_enabled(false),
enable_status(false) {
adv_raddr_timer = alarm_new_periodic("btm_ble.adv_raddr_timer");
}
~AdvertisingInstance() {
alarm_free(adv_raddr_timer);
if (timeout_timer) alarm_free(timeout_timer);
}
};
void btm_ble_adv_raddr_timer_timeout(void* data);
void DoNothing(uint8_t) {}
void DoNothing2(uint8_t, uint8_t) {}
struct closure_data {
base::Closure user_task;
tracked_objects::Location posted_from;
};
static void alarm_closure_cb(void* p) {
closure_data* data = (closure_data*)p;
VLOG(1) << "executing timer scheduled at %s" << data->posted_from.ToString();
data->user_task.Run();
delete data;
}
// Periodic alarms are not supported, because we clean up data in callback
void alarm_set_closure(const tracked_objects::Location& posted_from,
alarm_t* alarm, period_ms_t interval_ms,
base::Closure user_task) {
closure_data* data = new closure_data;
data->posted_from = posted_from;
data->user_task = std::move(user_task);
VLOG(1) << "scheduling timer %s" << data->posted_from.ToString();
alarm_set_on_mloop(alarm, interval_ms, alarm_closure_cb, data);
}
class BleAdvertisingManagerImpl;
/* a temporary type for holding all the data needed in callbacks below*/
struct CreatorParams {
uint8_t inst_id;
BleAdvertisingManagerImpl* self;
IdTxPowerStatusCb cb;
tBTM_BLE_ADV_PARAMS params;
std::vector<uint8_t> advertise_data;
std::vector<uint8_t> scan_response_data;
tBLE_PERIODIC_ADV_PARAMS periodic_params;
std::vector<uint8_t> periodic_data;
uint16_t duration;
uint8_t maxExtAdvEvents;
RegisterCb timeout_cb;
};
using c_type = std::unique_ptr<CreatorParams>;
class BleAdvertisingManagerImpl
: public BleAdvertisingManager,
public BleAdvertiserHciInterface::AdvertisingEventObserver {
public:
BleAdvertisingManagerImpl(BleAdvertiserHciInterface* interface) {
this->hci_interface = interface;
hci_interface->ReadInstanceCount(
base::Bind(&BleAdvertisingManagerImpl::ReadInstanceCountCb,
base::Unretained(this)));
}
~BleAdvertisingManagerImpl() { adv_inst.clear(); }
void GetOwnAddress(uint8_t inst_id, GetAddressCallback cb) override {
cb.Run(adv_inst[inst_id].own_address_type, adv_inst[inst_id].own_address);
}
void ReadInstanceCountCb(uint8_t instance_count) {
this->inst_count = instance_count;
adv_inst.reserve(inst_count);
/* Initialize adv instance indices and IDs. */
for (uint8_t i = 0; i < inst_count; i++) {
adv_inst.emplace_back(i);
}
}
void OnRpaGenerationComplete(base::Callback<void(RawAddress)> cb,
uint8_t rand[8]) {
VLOG(1) << __func__;
RawAddress bda;
rand[2] &= (~BLE_RESOLVE_ADDR_MASK);
rand[2] |= BLE_RESOLVE_ADDR_MSB;
bda.address[2] = rand[0];
bda.address[1] = rand[1];
bda.address[0] = rand[2];
BT_OCTET16 irk;
BTM_GetDeviceIDRoot(irk);
tSMP_ENC output;
if (!SMP_Encrypt(irk, BT_OCTET16_LEN, rand, 3, &output))
LOG_ASSERT(false) << "SMP_Encrypt failed";
/* set hash to be LSB of rpAddress */
bda.address[5] = output.param_buf[0];
bda.address[4] = output.param_buf[1];
bda.address[3] = output.param_buf[2];
cb.Run(bda);
}
void GenerateRpa(base::Callback<void(RawAddress)> cb) {
btm_gen_resolvable_private_addr(
Bind(&BleAdvertisingManagerImpl::OnRpaGenerationComplete,
base::Unretained(this), std::move(cb)));
}
void ConfigureRpa(AdvertisingInstance* p_inst, MultiAdvCb configuredCb) {
/* Connectable advertising set must be disabled when updating RPA */
bool restart = p_inst->IsEnabled() && p_inst->IsConnectable();
// If there is any form of timeout on the set, schedule address update when
// the set stops, because there is no good way to compute new timeout value.
// Maximum duration value is around 10 minutes, so this is safe.
if (restart && (p_inst->duration || p_inst->maxExtAdvEvents)) {
p_inst->address_update_required = true;
configuredCb.Run(0x01);
return;
}
GenerateRpa(Bind(
[](AdvertisingInstance* p_inst, MultiAdvCb configuredCb,
RawAddress bda) {
/* Connectable advertising set must be disabled when updating RPA */
bool restart = p_inst->IsEnabled() && p_inst->IsConnectable();
auto hci_interface =
((BleAdvertisingManagerImpl*)BleAdvertisingManager::Get())
->GetHciInterface();
if (restart) {
p_inst->enable_status = false;
hci_interface->Enable(false, p_inst->inst_id, 0x00, 0x00,
Bind(DoNothing));
}
/* set it to controller */
hci_interface->SetRandomAddress(
p_inst->inst_id, p_inst->own_address,
Bind(
[](AdvertisingInstance* p_inst, RawAddress bda,
MultiAdvCb configuredCb, uint8_t status) {
p_inst->own_address = bda;
configuredCb.Run(0x00);
},
p_inst, bda, configuredCb));
if (restart) {
p_inst->enable_status = true;
hci_interface->Enable(true, p_inst->inst_id, 0x00, 0x00,
Bind(DoNothing));
}
},
p_inst, std::move(configuredCb)));
}
void RegisterAdvertiser(
base::Callback<void(uint8_t /* inst_id */, uint8_t /* status */)> cb)
override {
AdvertisingInstance* p_inst = &adv_inst[0];
for (uint8_t i = 0; i < inst_count; i++, p_inst++) {
if (p_inst->in_use) continue;
p_inst->in_use = true;
// set up periodic timer to update address.
if (BTM_BleLocalPrivacyEnabled()) {
p_inst->own_address_type = BLE_ADDR_RANDOM;
GenerateRpa(Bind(
[](AdvertisingInstance* p_inst,
base::Callback<void(uint8_t /* inst_id */, uint8_t /* status */)>
cb,
RawAddress bda) {
p_inst->own_address = bda;
alarm_set_on_mloop(p_inst->adv_raddr_timer,
BTM_BLE_PRIVATE_ADDR_INT_MS,
btm_ble_adv_raddr_timer_timeout, p_inst);
cb.Run(p_inst->inst_id, BTM_BLE_MULTI_ADV_SUCCESS);
},
p_inst, cb));
} else {
p_inst->own_address_type = BLE_ADDR_PUBLIC;
p_inst->own_address = *controller_get_interface()->get_address();
cb.Run(p_inst->inst_id, BTM_BLE_MULTI_ADV_SUCCESS);
}
return;
}
LOG(INFO) << "no free advertiser instance";
cb.Run(0xFF, ADVERTISE_FAILED_TOO_MANY_ADVERTISERS);
}
void StartAdvertising(uint8_t advertiser_id, MultiAdvCb cb,
tBTM_BLE_ADV_PARAMS* params,
std::vector<uint8_t> advertise_data,
std::vector<uint8_t> scan_response_data, int duration,
MultiAdvCb timeout_cb) override {
/* a temporary type for holding all the data needed in callbacks below*/
struct CreatorParams {
uint8_t inst_id;
BleAdvertisingManagerImpl* self;
MultiAdvCb cb;
tBTM_BLE_ADV_PARAMS params;
std::vector<uint8_t> advertise_data;
std::vector<uint8_t> scan_response_data;
int duration;
MultiAdvCb timeout_cb;
};
std::unique_ptr<CreatorParams> c;
c.reset(new CreatorParams());
c->self = this;
c->cb = std::move(cb);
c->params = *params;
c->advertise_data = std::move(advertise_data);
c->scan_response_data = std::move(scan_response_data);
c->duration = duration;
c->timeout_cb = std::move(timeout_cb);
c->inst_id = advertiser_id;
using c_type = std::unique_ptr<CreatorParams>;
// this code is intentionally left formatted this way to highlight the
// asynchronous flow
// clang-format off
c->self->SetParameters(c->inst_id, &c->params, Bind(
[](c_type c, uint8_t status, int8_t tx_power) {
if (status != 0) {
LOG(ERROR) << "setting parameters failed, status: " << +status;
c->cb.Run(status);
return;
}
c->self->adv_inst[c->inst_id].tx_power = tx_power;
const RawAddress& rpa = c->self->adv_inst[c->inst_id].own_address;
c->self->GetHciInterface()->SetRandomAddress(c->inst_id, rpa, Bind(
[](c_type c, uint8_t status) {
if (status != 0) {
LOG(ERROR) << "setting random address failed, status: " << +status;
c->cb.Run(status);
return;
}
c->self->SetData(c->inst_id, false, std::move(c->advertise_data), Bind(
[](c_type c, uint8_t status) {
if (status != 0) {
LOG(ERROR) << "setting advertise data failed, status: " << +status;
c->cb.Run(status);
return;
}
c->self->SetData(c->inst_id, true, std::move(c->scan_response_data), Bind(
[](c_type c, uint8_t status) {
if (status != 0) {
LOG(ERROR) << "setting scan response data failed, status: " << +status;
c->cb.Run(status);
return;
}
c->self->Enable(c->inst_id, true, c->cb, c->duration, 0, std::move(c->timeout_cb));
}, base::Passed(&c)));
}, base::Passed(&c)));
}, base::Passed(&c)));
}, base::Passed(&c)));
// clang-format on
}
void StartAdvertisingSet(IdTxPowerStatusCb cb, tBTM_BLE_ADV_PARAMS* params,
std::vector<uint8_t> advertise_data,
std::vector<uint8_t> scan_response_data,
tBLE_PERIODIC_ADV_PARAMS* periodic_params,
std::vector<uint8_t> periodic_data,
uint16_t duration, uint8_t maxExtAdvEvents,
RegisterCb timeout_cb) override {
std::unique_ptr<CreatorParams> c;
c.reset(new CreatorParams());
c->self = this;
c->cb = std::move(cb);
c->params = *params;
c->advertise_data = std::move(advertise_data);
c->scan_response_data = std::move(scan_response_data);
c->periodic_params = *periodic_params;
c->periodic_data = std::move(periodic_data);
c->duration = duration;
c->maxExtAdvEvents = maxExtAdvEvents;
c->timeout_cb = std::move(timeout_cb);
// this code is intentionally left formatted this way to highlight the
// asynchronous flow
// clang-format off
c->self->RegisterAdvertiser(Bind(
[](c_type c, uint8_t advertiser_id, uint8_t status) {
if (status != 0) {
LOG(ERROR) << "registering advertiser failed, status: " << +status;
c->cb.Run(0, 0, status);
return;
}
c->inst_id = advertiser_id;
c->self->SetParameters(c->inst_id, &c->params, Bind(
[](c_type c, uint8_t status, int8_t tx_power) {
if (status != 0) {
c->self->Unregister(c->inst_id);
LOG(ERROR) << "setting parameters failed, status: " << +status;
c->cb.Run(0, 0, status);
return;
}
c->self->adv_inst[c->inst_id].tx_power = tx_power;
const RawAddress& rpa = c->self->adv_inst[c->inst_id].own_address;
c->self->GetHciInterface()->SetRandomAddress(c->inst_id, rpa, Bind(
[](c_type c, uint8_t status) {
if (status != 0) {
c->self->Unregister(c->inst_id);
LOG(ERROR) << "setting random address failed, status: " << +status;
c->cb.Run(0, 0, status);
return;
}
c->self->SetData(c->inst_id, false, std::move(c->advertise_data), Bind(
[](c_type c, uint8_t status) {
if (status != 0) {
c->self->Unregister(c->inst_id);
LOG(ERROR) << "setting advertise data failed, status: " << +status;
c->cb.Run(0, 0, status);
return;
}
c->self->SetData(c->inst_id, true, std::move(c->scan_response_data), Bind(
[](c_type c, uint8_t status) {
if (status != 0) {
c->self->Unregister(c->inst_id);
LOG(ERROR) << "setting scan response data failed, status: " << +status;
c->cb.Run(0, 0, status);
return;
}
if (c->periodic_params.enable) {
c->self->StartAdvertisingSetPeriodicPart(std::move(c));
} else {
c->self->StartAdvertisingSetFinish(std::move(c));
}
}, base::Passed(&c)));
}, base::Passed(&c)));
}, base::Passed(&c)));
}, base::Passed(&c)));
}, base::Passed(&c)));
// clang-format on
}
void StartAdvertisingSetPeriodicPart(c_type c) {
// this code is intentionally left formatted this way to highlight the
// asynchronous flow
// clang-format off
c->self->SetPeriodicAdvertisingParameters(c->inst_id, &c->periodic_params, Bind(
[](c_type c, uint8_t status) {
if (status != 0) {
c->self->Unregister(c->inst_id);
LOG(ERROR) << "setting periodic parameters failed, status: " << +status;
c->cb.Run(0, 0, status);
return;
}
c->self->SetPeriodicAdvertisingData(c->inst_id, std::move(c->periodic_data), Bind(
[](c_type c, uint8_t status) {
if (status != 0) {
c->self->Unregister(c->inst_id);
LOG(ERROR) << "setting periodic parameters failed, status: " << +status;
c->cb.Run(0, 0, status);
return;
}
c->self->SetPeriodicAdvertisingEnable(c->inst_id, true, Bind(
[](c_type c, uint8_t status) {
if (status != 0) {
c->self->Unregister(c->inst_id);
LOG(ERROR) << "enabling periodic advertising failed, status: " << +status;
c->cb.Run(0, 0, status);
return;
}
c->self->StartAdvertisingSetFinish(std::move(c));
}, base::Passed(&c)));
}, base::Passed(&c)));
}, base::Passed(&c)));
// clang-format on
}
void StartAdvertisingSetFinish(c_type c) {
uint8_t inst_id = c->inst_id;
uint16_t duration = c->duration;
uint8_t maxExtAdvEvents = c->maxExtAdvEvents;
RegisterCb timeout_cb = std::move(c->timeout_cb);
BleAdvertisingManagerImpl* self = c->self;
MultiAdvCb enable_cb = Bind(
[](c_type c, uint8_t status) {
if (status != 0) {
c->self->Unregister(c->inst_id);
LOG(ERROR) << "enabling advertiser failed, status: " << +status;
c->cb.Run(0, 0, status);
return;
}
int8_t tx_power = c->self->adv_inst[c->inst_id].tx_power;
c->cb.Run(c->inst_id, tx_power, status);
},
base::Passed(&c));
self->Enable(inst_id, true, std::move(enable_cb), duration, maxExtAdvEvents,
Bind(std::move(timeout_cb), inst_id));
}
void EnableWithTimerCb(uint8_t inst_id, MultiAdvCb enable_cb, int duration,
MultiAdvCb timeout_cb, uint8_t status) {
VLOG(1) << __func__ << " inst_id: " << +inst_id;
AdvertisingInstance* p_inst = &adv_inst[inst_id];
// Run the regular enable callback
enable_cb.Run(status);
p_inst->timeout_timer = alarm_new("btm_ble.adv_timeout");
base::Closure cb = Bind(&BleAdvertisingManagerImpl::Enable,
base::Unretained(this), inst_id, 0 /* disable */,
std::move(timeout_cb), 0, 0, base::Bind(DoNothing));
// schedule disable when the timeout passes
alarm_set_closure(FROM_HERE, p_inst->timeout_timer, duration * 10,
std::move(cb));
}
void Enable(uint8_t inst_id, bool enable, MultiAdvCb cb, uint16_t duration,
uint8_t maxExtAdvEvents, MultiAdvCb timeout_cb) override {
VLOG(1) << __func__ << " inst_id: " << +inst_id;
if (inst_id >= inst_count) {
LOG(ERROR) << "bad instance id " << +inst_id;
return;
}
AdvertisingInstance* p_inst = &adv_inst[inst_id];
VLOG(1) << __func__ << " enable: " << enable << ", duration: " << +duration;
if (!p_inst->in_use) {
LOG(ERROR) << "Invalid or no active instance";
cb.Run(BTM_BLE_MULTI_ADV_FAILURE);
return;
}
if (enable && (duration || maxExtAdvEvents)) {
p_inst->timeout_cb = std::move(timeout_cb);
}
p_inst->duration = duration;
p_inst->maxExtAdvEvents = maxExtAdvEvents;
if (enable && p_inst->address_update_required) {
p_inst->address_update_required = false;
ConfigureRpa(p_inst, base::Bind(&BleAdvertisingManagerImpl::EnableFinish,
base::Unretained(this), p_inst, enable,
std::move(cb)));
return;
}
EnableFinish(p_inst, enable, std::move(cb), 0);
}
void EnableFinish(AdvertisingInstance* p_inst, bool enable, MultiAdvCb cb,
uint8_t status) {
MultiAdvCb myCb;
if (enable && p_inst->duration) {
// TODO(jpawlowski): HCI implementation that can't do duration should
// emulate it, not EnableWithTimerCb.
myCb = Bind(&BleAdvertisingManagerImpl::EnableWithTimerCb,
base::Unretained(this), p_inst->inst_id, std::move(cb),
p_inst->duration, p_inst->timeout_cb);
} else {
myCb = std::move(cb);
if (p_inst->timeout_timer) {
alarm_cancel(p_inst->timeout_timer);
alarm_free(p_inst->timeout_timer);
p_inst->timeout_timer = nullptr;
}
}
if (enable) p_inst->enable_time = TimeTicks::Now();
p_inst->enable_status = enable;
GetHciInterface()->Enable(enable, p_inst->inst_id, p_inst->duration,
p_inst->maxExtAdvEvents, std::move(myCb));
}
void SetParameters(uint8_t inst_id, tBTM_BLE_ADV_PARAMS* p_params,
ParametersCb cb) override {
VLOG(1) << __func__ << " inst_id: " << +inst_id;
if (inst_id >= inst_count) {
LOG(ERROR) << "bad instance id " << +inst_id;
return;
}
AdvertisingInstance* p_inst = &adv_inst[inst_id];
if (!p_inst->in_use) {
LOG(ERROR) << "adv instance not in use" << +inst_id;
cb.Run(BTM_BLE_MULTI_ADV_FAILURE, 0);
return;
}
// TODO: disable only if was enabled, currently no use scenario needs
// that,
// we always set parameters before enabling
// GetHciInterface()->Enable(false, inst_id, Bind(DoNothing));
p_inst->advertising_event_properties =
p_params->advertising_event_properties;
p_inst->tx_power = p_params->tx_power;
p_inst->advertising_interval = p_params->adv_int_min;
const RawAddress& peer_address = RawAddress::kEmpty;
GetHciInterface()->SetParameters(
p_inst->inst_id, p_params->advertising_event_properties,
p_params->adv_int_min, p_params->adv_int_max, p_params->channel_map,
p_inst->own_address_type, p_inst->own_address, 0x00, peer_address,
p_params->adv_filter_policy, p_inst->tx_power,
p_params->primary_advertising_phy, 0x00,
p_params->secondary_advertising_phy, 0x01 /* TODO: proper SID */,
p_params->scan_request_notification_enable, cb);
// TODO: re-enable only if it was enabled, properly call
// SetParamsCallback
// currently no use scenario needs that
// GetHciInterface()->Enable(true, inst_id, BTM_BleUpdateAdvInstParamCb);
}
void SetData(uint8_t inst_id, bool is_scan_rsp, std::vector<uint8_t> data,
MultiAdvCb cb) override {
VLOG(1) << __func__ << " inst_id: " << +inst_id;
if (inst_id >= inst_count) {
LOG(ERROR) << "bad instance id " << +inst_id;
return;
}
AdvertisingInstance* p_inst = &adv_inst[inst_id];
VLOG(1) << "is_scan_rsp = " << is_scan_rsp;
if (!is_scan_rsp && is_connectable(p_inst->advertising_event_properties)) {
uint8_t flags_val = BTM_GENERAL_DISCOVERABLE;
if (p_inst->duration) flags_val = BTM_LIMITED_DISCOVERABLE;
std::vector<uint8_t> flags;
flags.push_back(2); // length
flags.push_back(HCI_EIR_FLAGS_TYPE);
flags.push_back(flags_val);
data.insert(data.begin(), flags.begin(), flags.end());
}
// Find and fill TX Power with the correct value
if (data.size()) {
size_t i = 0;
while (i < data.size()) {
uint8_t type = data[i + 1];
if (type == HCI_EIR_TX_POWER_LEVEL_TYPE) {
data[i + 2] = adv_inst[inst_id].tx_power;
}
i += data[i] + 1;
}
}
VLOG(1) << "data is: " << base::HexEncode(data.data(), data.size());
DivideAndSendData(
inst_id, data, cb,
base::Bind(&BleAdvertisingManagerImpl::SetDataAdvDataSender,
base::Unretained(this), is_scan_rsp));
}
void SetDataAdvDataSender(uint8_t is_scan_rsp, uint8_t inst_id,
uint8_t operation, uint8_t length, uint8_t* data,
MultiAdvCb cb) {
if (is_scan_rsp)
GetHciInterface()->SetScanResponseData(inst_id, operation, 0x01, length,
data, cb);
else
GetHciInterface()->SetAdvertisingData(inst_id, operation, 0x01, length,
data, cb);
}
using DataSender = base::Callback<void(
uint8_t /*inst_id*/, uint8_t /* operation */, uint8_t /* length */,
uint8_t* /* data */, MultiAdvCb /* done */)>;
void DivideAndSendData(int inst_id, std::vector<uint8_t> data,
MultiAdvCb done_cb, DataSender sender) {
DivideAndSendDataRecursively(true, inst_id, std::move(data), 0,
std::move(done_cb), std::move(sender), 0);
}
static void DivideAndSendDataRecursively(bool isFirst, int inst_id,
std::vector<uint8_t> data,
int offset, MultiAdvCb done_cb,
DataSender sender, uint8_t status) {
constexpr uint8_t INTERMEDIATE =
0x00; // Intermediate fragment of fragmented data
constexpr uint8_t FIRST = 0x01; // First fragment of fragmented data
constexpr uint8_t LAST = 0x02; // Last fragment of fragmented data
constexpr uint8_t COMPLETE = 0x03; // Complete extended advertising data
int dataSize = (int)data.size();
if (status != 0 || (!isFirst && offset == dataSize)) {
/* if we got error writing data, or reached the end of data */
done_cb.Run(status);
return;
}
bool moreThanOnePacket = dataSize - offset > ADV_DATA_LEN_MAX;
uint8_t operation = isFirst ? moreThanOnePacket ? FIRST : COMPLETE
: moreThanOnePacket ? INTERMEDIATE : LAST;
int length = moreThanOnePacket ? ADV_DATA_LEN_MAX : dataSize - offset;
int newOffset = offset + length;
sender.Run(
inst_id, operation, length, data.data() + offset,
Bind(&BleAdvertisingManagerImpl::DivideAndSendDataRecursively, false,
inst_id, std::move(data), newOffset, std::move(done_cb), sender));
}
void SetPeriodicAdvertisingParameters(uint8_t inst_id,
tBLE_PERIODIC_ADV_PARAMS* params,
MultiAdvCb cb) override {
VLOG(1) << __func__ << " inst_id: " << +inst_id;
GetHciInterface()->SetPeriodicAdvertisingParameters(
inst_id, params->min_interval, params->max_interval,
params->periodic_advertising_properties, cb);
}
void SetPeriodicAdvertisingData(uint8_t inst_id, std::vector<uint8_t> data,
MultiAdvCb cb) override {
VLOG(1) << __func__ << " inst_id: " << +inst_id;
VLOG(1) << "data is: " << base::HexEncode(data.data(), data.size());
DivideAndSendData(
inst_id, data, cb,
base::Bind(&BleAdvertiserHciInterface::SetPeriodicAdvertisingData,
base::Unretained(GetHciInterface())));
}
void SetPeriodicAdvertisingEnable(uint8_t inst_id, uint8_t enable,
MultiAdvCb cb) override {
VLOG(1) << __func__ << " inst_id: " << +inst_id << ", enable: " << +enable;
AdvertisingInstance* p_inst = &adv_inst[inst_id];
if (!p_inst->in_use) {
LOG(ERROR) << "Invalid or not active instance";
cb.Run(BTM_BLE_MULTI_ADV_FAILURE);
return;
}
MultiAdvCb enable_cb = Bind(
[](AdvertisingInstance* p_inst, uint8_t enable, MultiAdvCb cb,
uint8_t status) {
VLOG(1) << "periodc adv enable cb: inst_id: " << +p_inst->inst_id
<< ", enable: " << +enable << ", status: " << std::hex
<< +status;
if (!status) p_inst->periodic_enabled = enable;
cb.Run(status);
},
p_inst, enable, std::move(cb));
GetHciInterface()->SetPeriodicAdvertisingEnable(enable, inst_id,
std::move(enable_cb));
}
void Unregister(uint8_t inst_id) override {
AdvertisingInstance* p_inst = &adv_inst[inst_id];
VLOG(1) << __func__ << " inst_id: " << +inst_id;
if (inst_id >= inst_count) {
LOG(ERROR) << "bad instance id " << +inst_id;
return;
}
if (adv_inst[inst_id].IsEnabled()) {
p_inst->enable_status = false;
GetHciInterface()->Enable(false, inst_id, 0x00, 0x00, Bind(DoNothing));
}
if (p_inst->periodic_enabled) {
p_inst->periodic_enabled = false;
GetHciInterface()->SetPeriodicAdvertisingEnable(false, inst_id,
Bind(DoNothing));
}
alarm_cancel(p_inst->adv_raddr_timer);
p_inst->in_use = false;
GetHciInterface()->RemoveAdvertisingSet(inst_id, Bind(DoNothing));
p_inst->address_update_required = false;
}
void RecomputeTimeout(AdvertisingInstance* inst, TimeTicks now) {
TimeDelta duration = now - inst->enable_time;
bool cb_fired = false;
if (inst->duration) {
int durationDone = (duration.InMilliseconds() / 10);
if (durationDone + 1 >= inst->duration) {
inst->enable_status = false;
inst->timeout_cb.Run(0 /* TODO: STATUS HERE?*/);
cb_fired = true;
} else {
inst->duration = inst->duration - durationDone;
}
}
if (inst->maxExtAdvEvents && !cb_fired) {
int eventsDone =
(duration.InMilliseconds() / (inst->advertising_interval * 5 / 8));
if (eventsDone + 1 >= inst->maxExtAdvEvents) {
inst->enable_status = false;
inst->timeout_cb.Run(0 /* TODO: STATUS HERE?*/);
} else {
inst->maxExtAdvEvents = inst->maxExtAdvEvents - eventsDone;
}
}
}
void Suspend() {
std::vector<SetEnableData> sets;
for (AdvertisingInstance& inst : adv_inst) {
if (!inst.in_use || !inst.enable_status) continue;
if (inst.duration || inst.maxExtAdvEvents)
RecomputeTimeout(&inst, TimeTicks::Now());
sets.emplace_back(SetEnableData{.handle = inst.inst_id});
}
if (!sets.empty()) GetHciInterface()->Enable(false, sets, Bind(DoNothing));
}
void Resume() override {
std::vector<SetEnableData> sets;
for (const AdvertisingInstance& inst : adv_inst) {
if (inst.in_use && inst.enable_status) {
sets.emplace_back(SetEnableData{
.handle = inst.inst_id,
.duration = inst.duration,
.max_extended_advertising_events = inst.maxExtAdvEvents});
}
}
if (!sets.empty()) GetHciInterface()->Enable(true, sets, Bind(DoNothing));
}
void OnAdvertisingSetTerminated(
uint8_t status, uint8_t advertising_handle, uint16_t connection_handle,
uint8_t num_completed_extended_adv_events) override {
AdvertisingInstance* p_inst = &adv_inst[advertising_handle];
VLOG(1) << __func__ << "status: 0x" << std::hex << +status
<< ", advertising_handle: 0x" << std::hex << +advertising_handle
<< ", connection_handle: 0x" << std::hex << +connection_handle;
if (status == 0x43 || status == 0x3C) {
// either duration elapsed, or maxExtAdvEvents reached
p_inst->enable_status = false;
if (p_inst->timeout_cb.is_null()) {
LOG(INFO) << __func__ << "No timeout callback";
return;
}
p_inst->timeout_cb.Run(status);
return;
}
if (BTM_BleLocalPrivacyEnabled() &&
advertising_handle <= BTM_BLE_MULTI_ADV_MAX) {
btm_acl_update_conn_addr(connection_handle, p_inst->own_address);
}
VLOG(1) << "reneabling advertising";
if (p_inst->in_use == true) {
// TODO(jpawlowski): we don't really allow to do directed advertising
// right now. This should probably be removed, check with Andre.
if ((p_inst->advertising_event_properties & 0x0C) == 0) {
/* directed advertising bits not set */
RecomputeTimeout(p_inst, TimeTicks::Now());
if (p_inst->enable_status) {
GetHciInterface()->Enable(true, advertising_handle, p_inst->duration,
p_inst->maxExtAdvEvents, Bind(DoNothing));
}
} else {
/* mark directed adv as disabled if adv has been stopped */
p_inst->in_use = false;
}
}
}
private:
BleAdvertiserHciInterface* GetHciInterface() { return hci_interface; }
BleAdvertiserHciInterface* hci_interface = nullptr;
std::vector<AdvertisingInstance> adv_inst;
uint8_t inst_count;
};
BleAdvertisingManager* instance;
void btm_ble_adv_raddr_timer_timeout(void* data) {
((BleAdvertisingManagerImpl*)BleAdvertisingManager::Get())
->ConfigureRpa((AdvertisingInstance*)data, base::Bind(DoNothing));
}
} // namespace
void BleAdvertisingManager::Initialize(BleAdvertiserHciInterface* interface) {
instance = new BleAdvertisingManagerImpl(interface);
}
bool BleAdvertisingManager::IsInitialized() { return instance; }
BleAdvertisingManager* BleAdvertisingManager::Get() {
CHECK(instance);
return instance;
};
void BleAdvertisingManager::CleanUp() {
delete instance;
instance = nullptr;
};
/**
* This function initialize the advertising manager.
**/
void btm_ble_adv_init() {
BleAdvertiserHciInterface::Initialize();
BleAdvertisingManager::Initialize(BleAdvertiserHciInterface::Get());
BleAdvertiserHciInterface::Get()->SetAdvertisingEventObserver(
(BleAdvertisingManagerImpl*)BleAdvertisingManager::Get());
if (BleAdvertiserHciInterface::Get()->QuirkAdvertiserZeroHandle()) {
// If handle 0 can't be used, register advertiser for it, but never use it.
BleAdvertisingManager::Get()->RegisterAdvertiser(Bind(DoNothing2));
}
}
/*******************************************************************************
*
* Function btm_ble_multi_adv_cleanup
*
* Description This function cleans up multi adv control block.
*
* Parameters
* Returns void
*
******************************************************************************/
void btm_ble_multi_adv_cleanup(void) {
BleAdvertisingManager::CleanUp();
BleAdvertiserHciInterface::CleanUp();
}
// TODO(jpawlowski): Find a nicer way to test RecomputeTimeout without exposing
// AdvertisingInstance
bool timeout_triggered = false;
void test_timeout_cb(uint8_t status) { timeout_triggered = true; }
// verify that if duration passed, or is about to pass, recomputation will shut
// down the advertiser completly
void testRecomputeTimeout1() {
auto manager = (BleAdvertisingManagerImpl*)BleAdvertisingManager::Get();
TimeTicks start = TimeTicks::Now();
TimeTicks end = start + TimeDelta::FromMilliseconds(111);
AdvertisingInstance test1(0);
test1.enable_status = true;
test1.enable_time = start;
test1.duration = 12 /*120ms*/;
test1.timeout_cb = Bind(&test_timeout_cb);
manager->RecomputeTimeout(&test1, end);
CHECK(timeout_triggered == true);
timeout_triggered = false;
CHECK(test1.enable_status == false);
}
// verify that duration and maxExtAdvEvents are properly adjusted when
// recomputing.
void testRecomputeTimeout2() {
auto manager = (BleAdvertisingManagerImpl*)BleAdvertisingManager::Get();
TimeTicks start = TimeTicks::Now();
TimeTicks end = start + TimeDelta::FromMilliseconds(250);
AdvertisingInstance test1(0);
test1.enable_status = true;
test1.enable_time = start;
test1.duration = 50 /*500ms*/;
test1.maxExtAdvEvents = 50;
test1.advertising_interval = 16 /* 10 ms */;
test1.timeout_cb = Bind(&test_timeout_cb);
manager->RecomputeTimeout(&test1, end);
CHECK(timeout_triggered == false);
CHECK(test1.enable_status == true);
CHECK(test1.duration == 25);
CHECK(test1.maxExtAdvEvents == 25);
}
// verify that if maxExtAdvEvents were sent, or are close to end, recomputation
// wil shut down the advertiser completly
void testRecomputeTimeout3() {
auto manager = (BleAdvertisingManagerImpl*)BleAdvertisingManager::Get();
TimeTicks start = TimeTicks::Now();
TimeTicks end = start + TimeDelta::FromMilliseconds(495);
AdvertisingInstance test1(0);
test1.enable_status = true;
test1.enable_time = start;
test1.maxExtAdvEvents = 50;
test1.advertising_interval = 16 /* 10 ms */;
test1.timeout_cb = Bind(&test_timeout_cb);
manager->RecomputeTimeout(&test1, end);
CHECK(timeout_triggered == true);
timeout_triggered = false;
CHECK(test1.enable_status == false);
}