blob: 76500ec7a89d1ff6bc7d093daa639df6e26abbdc [file] [log] [blame]
/******************************************************************************
*
* 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"
#if (BLE_INCLUDED == TRUE)
#include "ble_advertiser.h"
#include "ble_advertiser_hci_interface.h"
#include "btm_int_types.h"
using base::Bind;
using multiadv_cb = base::Callback<void(uint8_t /* status */)>;
struct AdvertisingInstance {
uint8_t inst_id;
bool in_use;
uint8_t adv_evt;
BD_ADDR rpa;
alarm_t *adv_raddr_timer;
int8_t tx_power;
int timeout_s;
MultiAdvCb timeout_cb;
alarm_t *timeout_timer;
AdvertisingInstance(int inst_id)
: inst_id(inst_id),
in_use(false),
adv_evt(0),
rpa{0},
tx_power(0),
timeout_s(0),
timeout_cb(),
timeout_timer(nullptr) {
adv_raddr_timer = alarm_new_periodic("btm_ble.adv_raddr_timer");
}
~AdvertisingInstance() {
alarm_free(adv_raddr_timer);
if (timeout_timer) alarm_free(timeout_timer);
}
};
/************************************************************************************
** Externs
************************************************************************************/
extern fixed_queue_t *btu_general_alarm_queue;
void DoNothing(uint8_t) {}
std::queue<base::Callback<void(tBTM_RAND_ENC *p)>> *rand_gen_inst_id = nullptr;
/* RPA generation completion callback for each adv instance. Will continue write
* the new RPA into controller. */
void btm_ble_multi_adv_gen_rpa_cmpl(tBTM_RAND_ENC *p) {
/* Retrieve the index of adv instance from stored Q */
base::Callback<void(tBTM_RAND_ENC * p)> cb = rand_gen_inst_id->front();
rand_gen_inst_id->pop();
cb.Run(p);
}
void btm_ble_adv_raddr_timer_timeout(void *data);
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 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 + 1);
}
}
void OnRpaGenerationComplete(uint8_t inst_id, tBTM_RAND_ENC *p) {
#if (SMP_INCLUDED == TRUE)
AdvertisingInstance *p_inst = &adv_inst[inst_id - 1];
LOG(INFO) << "inst_id = " << +p_inst->inst_id;
if (!p) return;
p->param_buf[2] &= (~BLE_RESOLVE_ADDR_MASK);
p->param_buf[2] |= BLE_RESOLVE_ADDR_MSB;
p_inst->rpa[2] = p->param_buf[0];
p_inst->rpa[1] = p->param_buf[1];
p_inst->rpa[0] = p->param_buf[2];
BT_OCTET16 irk;
BTM_GetDeviceIDRoot(irk);
tSMP_ENC output;
if (!SMP_Encrypt(irk, BT_OCTET16_LEN, p->param_buf, 3, &output))
LOG_ASSERT(false) << "SMP_Encrypt failed";
/* set hash to be LSB of rpAddress */
p_inst->rpa[5] = output.param_buf[0];
p_inst->rpa[4] = output.param_buf[1];
p_inst->rpa[3] = output.param_buf[2];
if (p_inst->inst_id != BTM_BLE_MULTI_ADV_DEFAULT_STD &&
p_inst->inst_id < inst_count) {
/* set it to controller */
GetHciInterface()->SetRandomAddress(p_inst->rpa, p_inst->inst_id,
Bind(DoNothing));
}
#endif
}
void ConfigureRpa(uint8_t inst_id) {
if (rand_gen_inst_id == nullptr)
rand_gen_inst_id =
new std::queue<base::Callback<void(tBTM_RAND_ENC * p)>>();
rand_gen_inst_id->push(
Bind(&BleAdvertisingManagerImpl::OnRpaGenerationComplete,
base::Unretained(this), inst_id));
btm_gen_resolvable_private_addr((void *)btm_ble_multi_adv_gen_rpa_cmpl);
}
void RegisterAdvertiser(
base::Callback<void(uint8_t /* inst_id */, uint8_t /* status */)> cb)
override {
if (inst_count == 0) {
LOG(ERROR) << "multi adv not supported";
cb.Run(0xFF, BTM_BLE_MULTI_ADV_FAILURE);
return;
}
AdvertisingInstance *p_inst = &adv_inst[0];
for (uint8_t i = 0; i < inst_count - 1;
i++, p_inst++) {
if (!p_inst->in_use) {
p_inst->in_use = TRUE;
#if (BLE_PRIVACY_SPT == TRUE)
// configure the address, and set up periodic timer to update it.
ConfigureRpa(p_inst->inst_id);
if (BTM_BleLocalPrivacyEnabled()) {
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);
}
#endif
cb.Run(p_inst->inst_id, BTM_BLE_MULTI_ADV_SUCCESS);
return;
}
}
LOG(INFO) << "no free advertiser instance";
cb.Run(0xFF, BTM_BLE_MULTI_ADV_FAILURE);
}
void EnableWithTimerCb(uint8_t inst_id, int timeout_s, MultiAdvCb timeout_cb,
uint8_t status) {
AdvertisingInstance *p_inst = &adv_inst[inst_id - 1];
p_inst->timeout_s = timeout_s;
p_inst->timeout_cb = std::move(timeout_cb);
p_inst->timeout_timer = alarm_new("btm_ble.adv_timeout");
alarm_set_on_queue(p_inst->timeout_timer, p_inst->timeout_s * 1000, nullptr,
p_inst, btu_general_alarm_queue);
}
void Enable(uint8_t inst_id, bool enable, MultiAdvCb cb, int timeout_s,
MultiAdvCb timeout_cb) {
AdvertisingInstance *p_inst = &adv_inst[inst_id - 1];
VLOG(1) << __func__ << " inst_id: " << +inst_id << ", enable: " << enable;
if (inst_count == 0) {
LOG(ERROR) << "multi adv not supported";
return;
}
if (!p_inst || !p_inst->in_use) {
LOG(ERROR) << "Invalid or no active instance";
cb.Run(BTM_BLE_MULTI_ADV_FAILURE);
return;
}
if (enable && timeout_s) {
GetHciInterface()->Enable(
enable, p_inst->inst_id,
Bind(&BleAdvertisingManagerImpl::EnableWithTimerCb,
base::Unretained(this), inst_id, timeout_s, 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;
}
GetHciInterface()->Enable(enable, p_inst->inst_id, cb);
}
}
void SetParameters(uint8_t inst_id, tBTM_BLE_ADV_PARAMS *p_params,
MultiAdvCb cb) override {
AdvertisingInstance *p_inst = &adv_inst[inst_id - 1];
VLOG(1) << __func__ << " inst_id:" << +inst_id;
if (inst_count == 0) {
LOG(ERROR) << "multi adv not supported";
return;
}
if (inst_id > inst_count || inst_id < 0 ||
inst_id == BTM_BLE_MULTI_ADV_DEFAULT_STD) {
LOG(ERROR) << "bad instance id " << +inst_id;
return;
}
if (!p_inst->in_use) {
LOG(ERROR) << "adv instance not in use" << +inst_id;
cb.Run(BTM_BLE_MULTI_ADV_FAILURE);
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));
uint8_t own_address_type = BLE_ADDR_PUBLIC;
BD_ADDR own_address;
#if (BLE_PRIVACY_SPT == TRUE)
if (BTM_BleLocalPrivacyEnabled()) {
own_address_type = BLE_ADDR_RANDOM;
memcpy(own_address, p_inst->rpa, BD_ADDR_LEN);
} else {
#else
{
#endif
memcpy(own_address, controller_get_interface()->get_address()->address,
BD_ADDR_LEN);
}
BD_ADDR dummy = {0, 0, 0, 0, 0, 0};
p_inst->adv_evt = p_params->adv_type;
p_inst->tx_power = p_params->tx_power;
GetHciInterface()->SetParameters(
p_params->adv_int_min, p_params->adv_int_max, p_params->adv_type,
own_address_type, own_address, 0, dummy, p_params->channel_map,
p_params->adv_filter_policy, p_inst->inst_id, p_inst->tx_power, 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 {
AdvertisingInstance *p_inst = &adv_inst[inst_id - 1];
VLOG(1) << "inst_id = " << +inst_id << ", is_scan_rsp = " << is_scan_rsp;
if (inst_count == 0) {
LOG(ERROR) << "multi adv not supported";
return;
}
if (!is_scan_rsp && p_inst->adv_evt != BTM_BLE_NON_CONNECT_EVT) {
uint8_t flags_val = BTM_GENERAL_DISCOVERABLE;
if (p_inst->timeout_s) 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) {
int8_t tx_power = adv_inst[inst_id - 1].tx_power;
data[i + 2] = tx_power;
}
i += data[i] + 1;
}
}
if (inst_id > inst_count || inst_id < 0 ||
inst_id == BTM_BLE_MULTI_ADV_DEFAULT_STD) {
LOG(ERROR) << "bad instance id " << +inst_id;
return;
}
VLOG(1) << "data is: " << base::HexEncode(data.data(), data.size());
if (is_scan_rsp) {
GetHciInterface()->SetScanResponseData(data.size(), data.data(), inst_id,
cb);
} else {
GetHciInterface()->SetAdvertisingData(data.size(), data.data(), inst_id,
cb);
}
}
void Unregister(uint8_t inst_id) override {
AdvertisingInstance *p_inst = &adv_inst[inst_id - 1];
VLOG(1) << __func__ << " inst_id: " << +inst_id;
if (inst_count == 0) {
LOG(ERROR) << "multi adv not supported";
return;
}
if (inst_id > inst_count || inst_id < 0 ||
inst_id == BTM_BLE_MULTI_ADV_DEFAULT_STD) {
LOG(ERROR) << "bad instance id " << +inst_id;
return;
}
// TODO(jpawlowski): only disable when enabled or enabling
GetHciInterface()->Enable(false, inst_id, Bind(DoNothing));
alarm_cancel(p_inst->adv_raddr_timer);
p_inst->in_use = false;
}
void OnAdvertisingStateChanged(uint8_t inst_id, uint8_t reason,
uint16_t conn_handle) override {
AdvertisingInstance *p_inst = &adv_inst[inst_id - 1];
VLOG(1) << __func__ << " inst_id: 0x" << std::hex << inst_id
<< ", reason: 0x" << std::hex << reason << ", conn_handle: 0x"
<< std::hex << conn_handle;
#if (BLE_PRIVACY_SPT == TRUE)
if (BTM_BleLocalPrivacyEnabled() && inst_id <= BTM_BLE_MULTI_ADV_MAX &&
inst_id != BTM_BLE_MULTI_ADV_DEFAULT_STD) {
btm_acl_update_conn_addr(conn_handle, p_inst->rpa);
}
#endif
if (inst_id < inst_count &&
inst_id != BTM_BLE_MULTI_ADV_DEFAULT_STD) {
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->adv_evt != BTM_BLE_CONNECT_DIR_EVT) {
GetHciInterface()->Enable(true, inst_id, Bind(DoNothing));
} else {
/* mark directed adv as disabled if adv has been stopped */
p_inst->in_use = false;
}
}
} else if (inst_id == BTM_BLE_MULTI_ADV_DEFAULT_STD) {
/* re-enable connectibility */
uint16_t conn_mode = BTM_ReadConnectability(nullptr, nullptr);
if (conn_mode == BTM_BLE_CONNECTABLE) {
btm_ble_set_connectability(conn_mode);
}
}
}
private:
BleAdvertiserHciInterface *GetHciInterface() { return hci_interface; }
BleAdvertiserHciInterface *hci_interface = nullptr;
std::vector<AdvertisingInstance> adv_inst;
uint8_t inst_count;
};
namespace {
BleAdvertisingManager *instance;
}
void BleAdvertisingManager::Initialize(BleAdvertiserHciInterface *interface) {
instance = new BleAdvertisingManagerImpl(interface);
}
BleAdvertisingManager *BleAdvertisingManager::Get() {
CHECK(instance);
return instance;
};
void BleAdvertisingManager::CleanUp() {
delete instance;
instance = nullptr;
};
void btm_ble_adv_raddr_timer_timeout(void *data) {
((BleAdvertisingManagerImpl *)BleAdvertisingManager::Get())
->ConfigureRpa(((AdvertisingInstance *)data)->inst_id);
}
/*******************************************************************************
**
** Function btm_ble_multi_adv_init
**
** Description This function initialize the multi adv control block.
**
** Parameters None
**
** Returns void
**
*******************************************************************************/
void btm_ble_multi_adv_init() {
BleAdvertiserHciInterface::Initialize();
BleAdvertisingManager::Initialize(
BleAdvertiserHciInterface::Get());
BleAdvertiserHciInterface::Get()->SetAdvertisingEventObserver(
(BleAdvertisingManagerImpl*)BleAdvertisingManager::Get());
}
/*******************************************************************************
**
** 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();
}
#endif