blob: ba31c81aa0a36de93f91cfc5cad9477064570833 [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 <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 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 */)>;
extern void btm_gen_resolvable_private_addr(
base::Callback<void(uint8_t[8])> cb);
extern fixed_queue_t* btu_general_alarm_queue;
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;
uint8_t maxExtAdvEvents;
alarm_t* timeout_timer;
uint8_t own_address_type;
BD_ADDR own_address;
MultiAdvCb timeout_cb;
bool address_update_required;
/* 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;
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{0},
address_update_required(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_on_queue(const tracked_objects::Location& posted_from,
alarm_t* alarm, period_ms_t interval_ms,
base::Closure user_task, fixed_queue_t* queue) {
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_queue(alarm, interval_ms, alarm_closure_cb, data, queue);
}
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 {
bt_bdaddr_t addr;
memcpy(addr.address, adv_inst[inst_id].own_address, BD_ADDR_LEN);
cb.Run(adv_inst[inst_id].own_address_type, addr);
}
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(bt_bdaddr_t)> cb,
uint8_t rand[8]) {
VLOG(1) << __func__;
bt_bdaddr_t 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(bt_bdaddr_t)> 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,
bt_bdaddr_t 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, bt_bdaddr_t bda,
MultiAdvCb configuredCb, uint8_t status) {
memcpy(p_inst->own_address, &bda, BD_ADDR_LEN);
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,
bt_bdaddr_t bda) {
memcpy(p_inst->own_address, &bda, BD_ADDR_LEN);
alarm_set_on_queue(p_inst->adv_raddr_timer,
BTM_BLE_PRIVATE_ADDR_INT_MS,
btm_ble_adv_raddr_timer_timeout, p_inst,
btu_general_alarm_queue);
cb.Run(p_inst->inst_id, BTM_BLE_MULTI_ADV_SUCCESS);
},
p_inst, cb));
} else {
p_inst->own_address_type = BLE_ADDR_PUBLIC;
memcpy(p_inst->own_address,
controller_get_interface()->get_address()->address, BD_ADDR_LEN);
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;
BD_ADDR *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;
BD_ADDR *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_on_queue(FROM_HERE, p_inst->timeout_timer, duration * 10,
std::move(cb), btu_general_alarm_queue);
}
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) {
if (enable && p_inst->duration) {
p_inst->enable_status = enable;
// TODO(jpawlowski): HCI implementation that can't do duration should
// emulate it, not EnableWithTimerCb.
GetHciInterface()->Enable(
enable, p_inst->inst_id, p_inst->duration, p_inst->maxExtAdvEvents,
Bind(&BleAdvertisingManagerImpl::EnableWithTimerCb,
base::Unretained(this), p_inst->inst_id, std::move(cb),
p_inst->duration, p_inst->timeout_cb));
} else {
if (p_inst->timeout_timer) {
alarm_cancel(p_inst->timeout_timer);
alarm_free(p_inst->timeout_timer);
p_inst->timeout_timer = nullptr;
}
p_inst->enable_status = enable;
GetHciInterface()->Enable(enable, p_inst->inst_id, p_inst->duration,
p_inst->maxExtAdvEvents, std::move(cb));
}
}
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;
BD_ADDR peer_address = {0, 0, 0, 0, 0, 0};
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, 0x01,
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;
GetHciInterface()->SetPeriodicAdvertisingEnable(enable, inst_id, 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));
}
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 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 */) {
GetHciInterface()->Enable(true, advertising_handle, 0x00, 0x00,
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);
}
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();
}