/****************************************************************************** * * Copyright 2017 The Android Open Source Project * Copyright 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 "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" #include "stack/btm/btm_ble_int.h" #include #include #include #include #include #include #include #include #include #include using base::Bind; using base::TimeDelta; using base::TimeTicks; using RegisterCb = base::Callback; using IdTxPowerStatusCb = base::Callback; using SetEnableData = BleAdvertiserHciInterface::SetEnableData; extern void btm_gen_resolvable_private_addr( base::Callback 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); adv_raddr_timer = nullptr; if (timeout_timer) { alarm_free(timeout_timer); timeout_timer = nullptr; } } }; void btm_ble_adv_raddr_timer_timeout(void* data); struct closure_data { base::Closure user_task; base::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 static void alarm_set_closure(const base::Location& posted_from, alarm_t* alarm, uint64_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; base::WeakPtr self; IdTxPowerStatusCb cb; tBTM_BLE_ADV_PARAMS params; std::vector advertise_data; std::vector scan_response_data; tBLE_PERIODIC_ADV_PARAMS periodic_params; std::vector periodic_data; uint16_t duration; uint8_t maxExtAdvEvents; RegisterCb timeout_cb; }; using c_type = std::unique_ptr; BleAdvertisingManager* instance; base::WeakPtr instance_weakptr; class BleAdvertisingManagerImpl : public BleAdvertisingManager, public BleAdvertiserHciInterface::AdvertisingEventObserver { public: BleAdvertisingManagerImpl(BleAdvertiserHciInterface* interface) : hci_interface(interface), weak_factory_(this) { hci_interface->ReadInstanceCount( base::Bind(&BleAdvertisingManagerImpl::ReadInstanceCountCb, weak_factory_.GetWeakPtr())); } ~BleAdvertisingManagerImpl() override { 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 GenerateRpa(base::Callback cb) { btm_gen_resolvable_private_addr(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, const RawAddress& bda) { /* Connectable advertising set must be disabled when updating RPA */ bool restart = p_inst->IsEnabled() && p_inst->IsConnectable(); if (!instance_weakptr.get()) return; auto hci_interface = instance_weakptr.get()->GetHciInterface(); if (restart) { p_inst->enable_status = false; hci_interface->Enable(false, p_inst->inst_id, 0x00, 0x00, base::DoNothing()); } /* set it to controller */ hci_interface->SetRandomAddress( p_inst->inst_id, bda, 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, base::DoNothing()); } }, p_inst, std::move(configuredCb))); } void RegisterAdvertiser( base::Callback 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 cb, const RawAddress& bda) { p_inst->own_address = bda; alarm_set_on_mloop(p_inst->adv_raddr_timer, btm_get_next_private_addrress_interval_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 advertise_data, std::vector 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; base::WeakPtr self; MultiAdvCb cb; tBTM_BLE_ADV_PARAMS params; std::vector advertise_data; std::vector scan_response_data; int duration; MultiAdvCb timeout_cb; }; std::unique_ptr c; c.reset(new CreatorParams()); c->self = weak_factory_.GetWeakPtr(); 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; // 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 (!c->self) { LOG(INFO) << "Stack was shut down"; return; } if (status) { 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 (!c->self) { LOG(INFO) << "Stack was shut down"; return; } 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 (!c->self) { LOG(INFO) << "Stack was shut down"; return; } 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 (!c->self) { LOG(INFO) << "Stack was shut down"; return; } 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 advertise_data, std::vector scan_response_data, tBLE_PERIODIC_ADV_PARAMS* periodic_params, std::vector periodic_data, uint16_t duration, uint8_t maxExtAdvEvents, RegisterCb timeout_cb) override { std::unique_ptr c; c.reset(new CreatorParams()); c->self = weak_factory_.GetWeakPtr(); 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 (!c->self) { LOG(INFO) << "Stack was shut down"; return; } if (status != 0) { LOG(ERROR) << " 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 (!c->self) { LOG(INFO) << "Stack was shut down"; return; } 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; if (c->self->adv_inst[c->inst_id].own_address_type == BLE_ADDR_PUBLIC) { auto self = c->self; self->StartAdvertisingSetAfterAddressPart(std::move(c)); return; } //own_address_type == BLE_ADDR_RANDOM 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 (!c->self) { LOG(INFO) << "Stack was shut down"; return; } if (status != 0) { c->self->Unregister(c->inst_id); LOG(ERROR) << "setting random address failed, status: " << +status; c->cb.Run(0, 0, status); return; } auto self = c->self; self->StartAdvertisingSetAfterAddressPart(std::move(c)); }, base::Passed(&c))); }, base::Passed(&c))); }, base::Passed(&c))); // clang-format on } void StartAdvertisingSetAfterAddressPart(c_type c) { c->self->SetData( c->inst_id, false, std::move(c->advertise_data), Bind( [](c_type c, uint8_t status) { if (!c->self) { LOG(INFO) << "Stack was shut down"; return; } 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 (!c->self) { LOG(INFO) << "Stack was shut down"; return; } 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; } auto self = c->self; if (c->periodic_params.enable) { self->StartAdvertisingSetPeriodicPart(std::move(c)); } else { self->StartAdvertisingSetFinish(std::move(c)); } }, base::Passed(&c))); }, base::Passed(&c))); } 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 (!c->self) { LOG(INFO) << "Stack was shut down"; return; } 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 (!c->self) { LOG(INFO) << "Stack was shut down"; return; } 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 (!c->self) { LOG(INFO) << "Stack was shut down"; return; } if (status != 0) { c->self->Unregister(c->inst_id); LOG(ERROR) << "enabling periodic advertising failed, status: " << +status; c->cb.Run(0, 0, status); return; } auto self = c->self; 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); base::WeakPtr self = c->self; MultiAdvCb enable_cb = Bind( [](c_type c, uint8_t status) { if (!c->self) { LOG(INFO) << "Stack was shut down"; return; } 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, weak_factory_.GetWeakPtr(), inst_id, 0 /* disable */, std::move(timeout_cb), 0, 0, base::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, weak_factory_.GetWeakPtr(), 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, weak_factory_.GetWeakPtr(), 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, base::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; // sid must be in range 0x00 to 0x0F. Since no controller supports more than // 16 advertisers, it's safe to make sid equal to inst_id. uint8_t sid = p_inst->inst_id % 0x0F; 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, 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 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 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. // The TX Power section is a 3 byte section. for (size_t i = 0; (i + 2) < data.size();) { if (data[i + 1] == 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, weak_factory_.GetWeakPtr(), 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 DivideAndSendData(int inst_id, std::vector 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 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; auto dataData = data.data(); sender.Run( inst_id, operation, length, dataData + 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 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, base::DoNothing()); } if (p_inst->periodic_enabled) { p_inst->periodic_enabled = false; GetHciInterface()->SetPeriodicAdvertisingEnable(false, inst_id, base::DoNothing()); } alarm_cancel(p_inst->adv_raddr_timer); p_inst->in_use = false; GetHciInterface()->RemoveAdvertisingSet(inst_id, base::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() override { std::vector 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, base::DoNothing()); } void Resume() override { std::vector 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, base::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: " << loghex(status) << ", advertising_handle: " << loghex(advertising_handle) << ", connection_handle: " << loghex(connection_handle); if (status == HCI_ERR_LIMIT_REACHED || status == HCI_ERR_ADVERTISING_TIMEOUT) { // 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) { // 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, base::DoNothing()); } } else { /* mark directed adv as disabled if adv has been stopped */ p_inst->in_use = false; } } } base::WeakPtr GetWeakPtr() { return weak_factory_.GetWeakPtr(); } void CancelAdvAlarms() { AdvertisingInstance* p_inst = &adv_inst[0]; for (uint8_t i = 0; i < inst_count; i++, p_inst++) { if (p_inst->timeout_timer) { alarm_cancel(p_inst->timeout_timer); } if (p_inst->adv_raddr_timer) { alarm_cancel(p_inst->adv_raddr_timer); } } } private: BleAdvertiserHciInterface* GetHciInterface() { return hci_interface; } BleAdvertiserHciInterface* hci_interface = nullptr; std::vector adv_inst; uint8_t inst_count; // Member variables should appear before the WeakPtrFactory, to ensure // that any WeakPtrs are invalidated before its members // variable's destructors are executed, rendering them invalid. base::WeakPtrFactory weak_factory_; }; void btm_ble_adv_raddr_timer_timeout(void* data) { BleAdvertisingManagerImpl* ptr = instance_weakptr.get(); if (ptr) ptr->ConfigureRpa((AdvertisingInstance*)data, base::DoNothing()); } } // namespace void BleAdvertisingManager::Initialize(BleAdvertiserHciInterface* interface) { instance = new BleAdvertisingManagerImpl(interface); instance_weakptr = ((BleAdvertisingManagerImpl*)instance)->GetWeakPtr(); } bool BleAdvertisingManager::IsInitialized() { return instance; } base::WeakPtr BleAdvertisingManager::Get() { return instance_weakptr; }; void BleAdvertisingManager::CleanUp() { if (instance_weakptr.get()) instance_weakptr.get()->CancelAdvAlarms(); 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().get()); if (BleAdvertiserHciInterface::Get()->QuirkAdvertiserZeroHandle()) { // If handle 0 can't be used, register advertiser for it, but never use it. BleAdvertisingManager::Get().get()->RegisterAdvertiser(base::DoNothing()); } } /******************************************************************************* * * 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().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); timeout_triggered = false; CHECK(!test1.enable_status); } // verify that duration and maxExtAdvEvents are properly adjusted when // recomputing. void testRecomputeTimeout2() { auto manager = (BleAdvertisingManagerImpl*)BleAdvertisingManager::Get().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); CHECK(test1.enable_status); 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().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); timeout_triggered = false; CHECK(!test1.enable_status); }