You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1277 lines
52 KiB
1277 lines
52 KiB
/*
|
|
* Copyright 2019 The Android Open Source Project
|
|
*
|
|
* 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 <memory>
|
|
#include <mutex>
|
|
|
|
#include "hci/acl_manager.h"
|
|
#include "hci/controller.h"
|
|
#include "hci/hci_layer.h"
|
|
#include "hci/hci_packets.h"
|
|
#include "hci/le_advertising_interface.h"
|
|
#include "hci/le_advertising_manager.h"
|
|
#include "module.h"
|
|
#include "os/handler.h"
|
|
#include "os/log.h"
|
|
|
|
namespace bluetooth {
|
|
namespace hci {
|
|
|
|
const ModuleFactory LeAdvertisingManager::Factory = ModuleFactory([]() { return new LeAdvertisingManager(); });
|
|
|
|
enum class AdvertisingApiType {
|
|
LEGACY = 1,
|
|
ANDROID_HCI = 2,
|
|
EXTENDED = 3,
|
|
};
|
|
|
|
enum class AdvertisingFlag : uint8_t {
|
|
LE_LIMITED_DISCOVERABLE = 0x01,
|
|
LE_GENERAL_DISCOVERABLE = 0x02,
|
|
BR_EDR_NOT_SUPPORTED = 0x04,
|
|
SIMULTANEOUS_LE_AND_BR_EDR_CONTROLLER = 0x08,
|
|
SIMULTANEOUS_LE_AND_BR_EDR_HOST = 0x10,
|
|
};
|
|
|
|
struct Advertiser {
|
|
os::Handler* handler;
|
|
AddressWithType current_address;
|
|
common::Callback<void(Address, AddressType)> scan_callback;
|
|
common::Callback<void(ErrorCode, uint8_t, uint8_t)> set_terminated_callback;
|
|
int8_t tx_power;
|
|
uint16_t duration;
|
|
uint8_t max_extended_advertising_events;
|
|
bool started = false;
|
|
bool connectable = false;
|
|
std::unique_ptr<os::Alarm> address_rotation_alarm;
|
|
};
|
|
|
|
ExtendedAdvertisingConfig::ExtendedAdvertisingConfig(const AdvertisingConfig& config) : AdvertisingConfig(config) {
|
|
switch (config.advertising_type) {
|
|
case AdvertisingType::ADV_IND:
|
|
connectable = true;
|
|
scannable = true;
|
|
break;
|
|
case AdvertisingType::ADV_DIRECT_IND:
|
|
connectable = true;
|
|
directed = true;
|
|
high_duty_directed_connectable = true;
|
|
break;
|
|
case AdvertisingType::ADV_SCAN_IND:
|
|
scannable = true;
|
|
break;
|
|
case AdvertisingType::ADV_NONCONN_IND:
|
|
break;
|
|
case AdvertisingType::ADV_DIRECT_IND_LOW:
|
|
connectable = true;
|
|
directed = true;
|
|
break;
|
|
default:
|
|
LOG_WARN("Unknown event type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
struct LeAdvertisingManager::impl : public bluetooth::hci::LeAddressManagerCallback {
|
|
impl(Module* module) : module_(module), le_advertising_interface_(nullptr), num_instances_(0) {}
|
|
|
|
~impl() {
|
|
if (address_manager_registered) {
|
|
le_address_manager_->Unregister(this);
|
|
}
|
|
advertising_sets_.clear();
|
|
}
|
|
|
|
void start(os::Handler* handler, hci::HciLayer* hci_layer, hci::Controller* controller,
|
|
hci::AclManager* acl_manager) {
|
|
module_handler_ = handler;
|
|
hci_layer_ = hci_layer;
|
|
controller_ = controller;
|
|
le_maximum_advertising_data_length_ = controller_->GetLeMaximumAdvertisingDataLength();
|
|
acl_manager_ = acl_manager;
|
|
le_address_manager_ = acl_manager->GetLeAddressManager();
|
|
le_advertising_interface_ =
|
|
hci_layer_->GetLeAdvertisingInterface(module_handler_->BindOn(this, &LeAdvertisingManager::impl::handle_event));
|
|
num_instances_ = controller_->GetLeNumberOfSupportedAdverisingSets();
|
|
enabled_sets_ = std::vector<EnabledSet>(num_instances_);
|
|
for (size_t i = 0; i < enabled_sets_.size(); i++) {
|
|
enabled_sets_[i].advertising_handle_ = kInvalidHandle;
|
|
}
|
|
|
|
if (controller_->IsSupported(hci::OpCode::LE_SET_EXTENDED_ADVERTISING_PARAMETERS)) {
|
|
advertising_api_type_ = AdvertisingApiType::EXTENDED;
|
|
} else if (controller_->IsSupported(hci::OpCode::LE_MULTI_ADVT)) {
|
|
advertising_api_type_ = AdvertisingApiType::ANDROID_HCI;
|
|
} else {
|
|
advertising_api_type_ = AdvertisingApiType::LEGACY;
|
|
hci_layer_->EnqueueCommand(
|
|
LeReadAdvertisingPhysicalChannelTxPowerBuilder::Create(),
|
|
handler->BindOnceOn(this, &impl::on_read_advertising_physical_channel_tx_power));
|
|
}
|
|
}
|
|
|
|
size_t GetNumberOfAdvertisingInstances() const {
|
|
return num_instances_;
|
|
}
|
|
|
|
AdvertisingApiType get_advertising_api_type() const {
|
|
return advertising_api_type_;
|
|
}
|
|
|
|
void register_advertising_callback(AdvertisingCallback* advertising_callback) {
|
|
advertising_callbacks_ = advertising_callback;
|
|
}
|
|
|
|
void handle_event(LeMetaEventView event) {
|
|
switch (event.GetSubeventCode()) {
|
|
case hci::SubeventCode::SCAN_REQUEST_RECEIVED:
|
|
handle_scan_request(LeScanRequestReceivedView::Create(event));
|
|
break;
|
|
case hci::SubeventCode::ADVERTISING_SET_TERMINATED:
|
|
handle_set_terminated(LeAdvertisingSetTerminatedView::Create(event));
|
|
break;
|
|
default:
|
|
LOG_INFO("Unknown subevent in scanner %s", hci::SubeventCodeText(event.GetSubeventCode()).c_str());
|
|
}
|
|
}
|
|
|
|
void handle_scan_request(LeScanRequestReceivedView event_view) {
|
|
if (!event_view.IsValid()) {
|
|
LOG_INFO("Dropping invalid scan request event");
|
|
return;
|
|
}
|
|
registered_handler_->Post(
|
|
common::BindOnce(scan_callback_, event_view.GetScannerAddress(), event_view.GetScannerAddressType()));
|
|
}
|
|
|
|
void handle_set_terminated(LeAdvertisingSetTerminatedView event_view) {
|
|
if (!event_view.IsValid()) {
|
|
LOG_INFO("Dropping invalid advertising event");
|
|
return;
|
|
}
|
|
|
|
uint8_t advertiser_id = event_view.GetAdvertisingHandle();
|
|
|
|
if (advertising_sets_[advertiser_id].address_rotation_alarm != nullptr) {
|
|
advertising_sets_[advertiser_id].address_rotation_alarm->Cancel();
|
|
advertising_sets_[advertiser_id].address_rotation_alarm.reset();
|
|
}
|
|
enabled_sets_[advertiser_id].advertising_handle_ = kInvalidHandle;
|
|
|
|
AddressWithType advertiser_address = advertising_sets_[event_view.GetAdvertisingHandle()].current_address;
|
|
|
|
acl_manager_->OnAdvertisingSetTerminated(
|
|
event_view.GetStatus(), event_view.GetConnectionHandle(), advertiser_address);
|
|
}
|
|
|
|
AdvertiserId allocate_advertiser() {
|
|
AdvertiserId id = 0;
|
|
{
|
|
std::unique_lock lock(id_mutex_);
|
|
while (id < num_instances_ && advertising_sets_.count(id) != 0) {
|
|
id++;
|
|
}
|
|
}
|
|
if (id == num_instances_) {
|
|
return kInvalidId;
|
|
}
|
|
return id;
|
|
}
|
|
|
|
void remove_advertiser(AdvertiserId advertiser_id) {
|
|
stop_advertising(advertiser_id);
|
|
std::unique_lock lock(id_mutex_);
|
|
if (advertising_sets_.count(advertiser_id) == 0) {
|
|
return;
|
|
}
|
|
if (advertising_sets_.empty() && address_manager_registered) {
|
|
le_address_manager_->Unregister(this);
|
|
address_manager_registered = false;
|
|
paused = false;
|
|
}
|
|
if (advertising_api_type_ == AdvertisingApiType::EXTENDED) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeRemoveAdvertisingSetBuilder::Create(advertiser_id),
|
|
module_handler_->BindOnce(impl::check_status<LeRemoveAdvertisingSetCompleteView>));
|
|
|
|
if (advertising_sets_[advertiser_id].address_rotation_alarm != nullptr) {
|
|
advertising_sets_[advertiser_id].address_rotation_alarm->Cancel();
|
|
advertising_sets_[advertiser_id].address_rotation_alarm.reset();
|
|
}
|
|
}
|
|
advertising_sets_.erase(advertiser_id);
|
|
}
|
|
|
|
void create_advertiser(
|
|
AdvertiserId id,
|
|
const AdvertisingConfig config,
|
|
const common::Callback<void(Address, AddressType)>& scan_callback,
|
|
const common::Callback<void(ErrorCode, uint8_t, uint8_t)>& set_terminated_callback,
|
|
os::Handler* handler) {
|
|
advertising_sets_[id].scan_callback = scan_callback;
|
|
advertising_sets_[id].set_terminated_callback = set_terminated_callback;
|
|
advertising_sets_[id].handler = handler;
|
|
advertising_sets_[id].current_address = AddressWithType{};
|
|
|
|
if (!address_manager_registered) {
|
|
le_address_manager_->Register(this);
|
|
address_manager_registered = true;
|
|
}
|
|
|
|
switch (advertising_api_type_) {
|
|
case (AdvertisingApiType::LEGACY): {
|
|
set_parameters(id, config);
|
|
if (config.advertising_type == AdvertisingType::ADV_IND ||
|
|
config.advertising_type == AdvertisingType::ADV_NONCONN_IND) {
|
|
set_data(id, true, config.scan_response);
|
|
}
|
|
set_data(id, false, config.advertisement);
|
|
if (!paused) {
|
|
enable_advertiser(id, true, 0, 0);
|
|
} else {
|
|
enabled_sets_[id].advertising_handle_ = id;
|
|
}
|
|
} break;
|
|
case (AdvertisingApiType::ANDROID_HCI): {
|
|
set_parameters(id, config);
|
|
if (config.advertising_type == AdvertisingType::ADV_IND ||
|
|
config.advertising_type == AdvertisingType::ADV_NONCONN_IND) {
|
|
set_data(id, true, config.scan_response);
|
|
}
|
|
set_data(id, false, config.advertisement);
|
|
advertising_sets_[id].current_address = le_address_manager_->GetAnotherAddress();
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeMultiAdvtSetRandomAddrBuilder::Create(advertising_sets_[id].current_address.GetAddress(), id),
|
|
module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
|
|
if (!paused) {
|
|
enable_advertiser(id, true, 0, 0);
|
|
} else {
|
|
enabled_sets_[id].advertising_handle_ = id;
|
|
}
|
|
} break;
|
|
case (AdvertisingApiType::EXTENDED): {
|
|
LOG_WARN("Unexpected AdvertisingApiType EXTENDED");
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void create_extended_advertiser(
|
|
int reg_id,
|
|
AdvertiserId id,
|
|
const ExtendedAdvertisingConfig config,
|
|
const common::Callback<void(Address, AddressType)>& scan_callback,
|
|
const common::Callback<void(ErrorCode, uint8_t, uint8_t)>& set_terminated_callback,
|
|
uint16_t duration,
|
|
uint8_t max_ext_adv_events,
|
|
os::Handler* handler) {
|
|
id_map_[id] = reg_id;
|
|
|
|
if (advertising_api_type_ != AdvertisingApiType::EXTENDED) {
|
|
create_advertiser(id, config, scan_callback, set_terminated_callback, handler);
|
|
return;
|
|
}
|
|
|
|
advertising_sets_[id].scan_callback = scan_callback;
|
|
advertising_sets_[id].set_terminated_callback = set_terminated_callback;
|
|
advertising_sets_[id].duration = duration;
|
|
advertising_sets_[id].max_extended_advertising_events = max_ext_adv_events;
|
|
advertising_sets_[id].handler = handler;
|
|
|
|
set_parameters(id, config);
|
|
|
|
auto address_policy = le_address_manager_->GetAddressPolicy();
|
|
if (config.own_address_type == OwnAddressType::RANDOM_DEVICE_ADDRESS) {
|
|
if (address_policy == LeAddressManager::AddressPolicy::USE_NON_RESOLVABLE_ADDRESS ||
|
|
address_policy == LeAddressManager::AddressPolicy::USE_RESOLVABLE_ADDRESS) {
|
|
AddressWithType address_with_type = le_address_manager_->GetAnotherAddress();
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingRandomAddressBuilder::Create(id, address_with_type.GetAddress()),
|
|
module_handler_->BindOnceOn(
|
|
this,
|
|
&impl::on_set_advertising_set_random_address_complete<
|
|
LeSetExtendedAdvertisingRandomAddressCompleteView>,
|
|
id,
|
|
address_with_type));
|
|
|
|
// start timer for random address
|
|
advertising_sets_[id].address_rotation_alarm = std::make_unique<os::Alarm>(module_handler_);
|
|
advertising_sets_[id].address_rotation_alarm->Schedule(
|
|
common::BindOnce(&impl::set_advertising_set_random_address, common::Unretained(this), id),
|
|
le_address_manager_->GetNextPrivateAddressIntervalMs());
|
|
} else {
|
|
advertising_sets_[id].current_address = le_address_manager_->GetCurrentAddress();
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingRandomAddressBuilder::Create(
|
|
id, advertising_sets_[id].current_address.GetAddress()),
|
|
module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingRandomAddressCompleteView>));
|
|
}
|
|
} else {
|
|
advertising_sets_[id].current_address =
|
|
AddressWithType(controller_->GetMacAddress(), AddressType::PUBLIC_DEVICE_ADDRESS);
|
|
}
|
|
if (config.advertising_type == AdvertisingType::ADV_IND ||
|
|
config.advertising_type == AdvertisingType::ADV_NONCONN_IND) {
|
|
set_data(id, true, config.scan_response);
|
|
}
|
|
set_data(id, false, config.advertisement);
|
|
|
|
if (!config.periodic_data.empty()) {
|
|
set_periodic_parameter(id, config.periodic_advertising_parameters);
|
|
set_periodic_data(id, config.periodic_data);
|
|
enable_periodic_advertising(id, true);
|
|
}
|
|
|
|
if (!paused) {
|
|
enable_advertiser(id, true, duration, max_ext_adv_events);
|
|
} else {
|
|
EnabledSet curr_set;
|
|
curr_set.advertising_handle_ = id;
|
|
curr_set.duration_ = duration;
|
|
curr_set.max_extended_advertising_events_ = max_ext_adv_events;
|
|
std::vector<EnabledSet> enabled_sets = {curr_set};
|
|
enabled_sets_[id] = curr_set;
|
|
}
|
|
}
|
|
|
|
void stop_advertising(AdvertiserId advertiser_id) {
|
|
if (advertising_sets_.find(advertiser_id) == advertising_sets_.end()) {
|
|
LOG_INFO("Unknown advertising set %u", advertiser_id);
|
|
return;
|
|
}
|
|
EnabledSet curr_set;
|
|
curr_set.advertising_handle_ = advertiser_id;
|
|
std::vector<EnabledSet> enabled_vector{curr_set};
|
|
|
|
// If advertising or periodic advertising on the advertising set is enabled,
|
|
// then the Controller will return the error code Command Disallowed (0x0C).
|
|
// Thus, we should disable it before removing it.
|
|
switch (advertising_api_type_) {
|
|
case (AdvertisingApiType::LEGACY):
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetAdvertisingEnableBuilder::Create(Enable::DISABLED),
|
|
module_handler_->BindOnce(impl::check_status<LeSetAdvertisingEnableCompleteView>));
|
|
break;
|
|
case (AdvertisingApiType::ANDROID_HCI):
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeMultiAdvtSetEnableBuilder::Create(Enable::DISABLED, advertiser_id),
|
|
module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
|
|
break;
|
|
case (AdvertisingApiType::EXTENDED): {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::DISABLED, enabled_vector),
|
|
module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>));
|
|
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetPeriodicAdvertisingEnableBuilder::Create(Enable::DISABLED, advertiser_id),
|
|
module_handler_->BindOnce(impl::check_status<LeSetPeriodicAdvertisingEnableCompleteView>));
|
|
} break;
|
|
}
|
|
|
|
std::unique_lock lock(id_mutex_);
|
|
enabled_sets_[advertiser_id].advertising_handle_ = kInvalidHandle;
|
|
}
|
|
|
|
void set_advertising_set_random_address(AdvertiserId advertiser_id) {
|
|
// This function should only be trigger by enabled advertising set
|
|
if (enabled_sets_[advertiser_id].advertising_handle_ == kInvalidHandle) {
|
|
if (advertising_sets_[advertiser_id].address_rotation_alarm != nullptr) {
|
|
advertising_sets_[advertiser_id].address_rotation_alarm->Cancel();
|
|
advertising_sets_[advertiser_id].address_rotation_alarm.reset();
|
|
}
|
|
return;
|
|
}
|
|
|
|
// TODO handle duration and max_extended_advertising_events_
|
|
EnabledSet curr_set;
|
|
curr_set.advertising_handle_ = advertiser_id;
|
|
curr_set.duration_ = advertising_sets_[advertiser_id].duration;
|
|
curr_set.max_extended_advertising_events_ = advertising_sets_[advertiser_id].max_extended_advertising_events;
|
|
std::vector<EnabledSet> enabled_sets = {curr_set};
|
|
|
|
// For connectable advertising, we should disable it first
|
|
if (advertising_sets_[advertiser_id].connectable) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::DISABLED, enabled_sets),
|
|
module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>));
|
|
}
|
|
|
|
AddressWithType address_with_type = le_address_manager_->GetAnotherAddress();
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingRandomAddressBuilder::Create(advertiser_id, address_with_type.GetAddress()),
|
|
module_handler_->BindOnceOn(
|
|
this,
|
|
&impl::on_set_advertising_set_random_address_complete<LeSetExtendedAdvertisingRandomAddressCompleteView>,
|
|
advertiser_id,
|
|
address_with_type));
|
|
|
|
if (advertising_sets_[advertiser_id].connectable) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::ENABLED, enabled_sets),
|
|
module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingEnableCompleteView>));
|
|
}
|
|
|
|
advertising_sets_[advertiser_id].address_rotation_alarm->Schedule(
|
|
common::BindOnce(&impl::set_advertising_set_random_address, common::Unretained(this), advertiser_id),
|
|
le_address_manager_->GetNextPrivateAddressIntervalMs());
|
|
}
|
|
|
|
void set_parameters(AdvertiserId advertiser_id, ExtendedAdvertisingConfig config) {
|
|
advertising_sets_[advertiser_id].connectable = config.connectable;
|
|
advertising_sets_[advertiser_id].tx_power = config.tx_power;
|
|
|
|
switch (advertising_api_type_) {
|
|
case (AdvertisingApiType::LEGACY): {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetAdvertisingParametersBuilder::Create(
|
|
config.interval_min,
|
|
config.interval_max,
|
|
config.advertising_type,
|
|
config.own_address_type,
|
|
config.peer_address_type,
|
|
config.peer_address,
|
|
config.channel_map,
|
|
config.filter_policy),
|
|
module_handler_->BindOnceOn(
|
|
this, &impl::check_status_with_id<LeSetAdvertisingParametersCompleteView>, advertiser_id));
|
|
} break;
|
|
case (AdvertisingApiType::ANDROID_HCI): {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeMultiAdvtParamBuilder::Create(
|
|
config.interval_min,
|
|
config.interval_max,
|
|
config.advertising_type,
|
|
config.own_address_type,
|
|
config.peer_address_type,
|
|
config.peer_address,
|
|
config.channel_map,
|
|
config.filter_policy,
|
|
advertiser_id,
|
|
config.tx_power),
|
|
module_handler_->BindOnceOn(this, &impl::check_status_with_id<LeMultiAdvtCompleteView>, advertiser_id));
|
|
} break;
|
|
case (AdvertisingApiType::EXTENDED): {
|
|
// sid must be in range 0x00 to 0x0F. Since no controller supports more than
|
|
// 16 advertisers, it's safe to make sid equal to id.
|
|
config.sid = advertiser_id % kAdvertisingSetIdMask;
|
|
|
|
if (config.legacy_pdus) {
|
|
LegacyAdvertisingProperties legacy_properties = LegacyAdvertisingProperties::ADV_IND;
|
|
if (config.connectable && config.directed) {
|
|
if (config.high_duty_directed_connectable) {
|
|
legacy_properties = LegacyAdvertisingProperties::ADV_DIRECT_IND_HIGH;
|
|
} else {
|
|
legacy_properties = LegacyAdvertisingProperties::ADV_DIRECT_IND_LOW;
|
|
}
|
|
}
|
|
if (config.scannable && !config.connectable) {
|
|
legacy_properties = LegacyAdvertisingProperties::ADV_SCAN_IND;
|
|
}
|
|
if (!config.scannable && !config.connectable) {
|
|
legacy_properties = LegacyAdvertisingProperties::ADV_NONCONN_IND;
|
|
}
|
|
|
|
le_advertising_interface_->EnqueueCommand(
|
|
LeSetExtendedAdvertisingLegacyParametersBuilder::Create(
|
|
advertiser_id,
|
|
legacy_properties,
|
|
config.interval_min,
|
|
config.interval_max,
|
|
config.channel_map,
|
|
config.own_address_type,
|
|
config.peer_address_type,
|
|
config.peer_address,
|
|
config.filter_policy,
|
|
config.tx_power,
|
|
config.sid,
|
|
config.enable_scan_request_notifications),
|
|
module_handler_->BindOnceOn(
|
|
this,
|
|
&impl::on_set_extended_advertising_parameters_complete<
|
|
LeSetExtendedAdvertisingParametersCompleteView>,
|
|
advertiser_id));
|
|
} else {
|
|
uint8_t legacy_properties = (config.connectable ? 0x1 : 0x00) | (config.scannable ? 0x2 : 0x00) |
|
|
(config.directed ? 0x4 : 0x00) |
|
|
(config.high_duty_directed_connectable ? 0x8 : 0x00);
|
|
uint8_t extended_properties = (config.anonymous ? 0x20 : 0x00) | (config.include_tx_power ? 0x40 : 0x00);
|
|
extended_properties = extended_properties >> 5;
|
|
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingParametersBuilder::Create(
|
|
advertiser_id,
|
|
legacy_properties,
|
|
extended_properties,
|
|
config.interval_min,
|
|
config.interval_max,
|
|
config.channel_map,
|
|
config.own_address_type,
|
|
config.peer_address_type,
|
|
config.peer_address,
|
|
config.filter_policy,
|
|
config.tx_power,
|
|
(config.use_le_coded_phy ? PrimaryPhyType::LE_CODED : PrimaryPhyType::LE_1M),
|
|
config.secondary_max_skip,
|
|
config.secondary_advertising_phy,
|
|
config.sid,
|
|
config.enable_scan_request_notifications),
|
|
module_handler_->BindOnceOn(
|
|
this,
|
|
&impl::on_set_extended_advertising_parameters_complete<
|
|
LeSetExtendedAdvertisingParametersCompleteView>,
|
|
advertiser_id));
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void set_data(AdvertiserId advertiser_id, bool set_scan_rsp, std::vector<GapData> data) {
|
|
if (!set_scan_rsp && advertising_sets_[advertiser_id].connectable) {
|
|
GapData gap_data;
|
|
gap_data.data_type_ = GapDataType::FLAGS;
|
|
if (advertising_sets_[advertiser_id].duration == 0) {
|
|
gap_data.data_.push_back(static_cast<uint8_t>(AdvertisingFlag::LE_GENERAL_DISCOVERABLE));
|
|
} else {
|
|
gap_data.data_.push_back(static_cast<uint8_t>(AdvertisingFlag::LE_LIMITED_DISCOVERABLE));
|
|
}
|
|
data.insert(data.begin(), gap_data);
|
|
}
|
|
|
|
// Find and fill TX Power with the correct value.
|
|
for (auto& gap_data : data) {
|
|
if (gap_data.data_type_ == GapDataType::TX_POWER_LEVEL) {
|
|
gap_data.data_[0] = advertising_sets_[advertiser_id].tx_power;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (advertising_api_type_) {
|
|
case (AdvertisingApiType::LEGACY): {
|
|
if (set_scan_rsp) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetScanResponseDataBuilder::Create(data),
|
|
module_handler_->BindOnceOn(
|
|
this, &impl::check_status_with_id<LeSetScanResponseDataCompleteView>, advertiser_id));
|
|
} else {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetAdvertisingDataBuilder::Create(data),
|
|
module_handler_->BindOnceOn(
|
|
this, &impl::check_status_with_id<LeSetAdvertisingDataCompleteView>, advertiser_id));
|
|
}
|
|
} break;
|
|
case (AdvertisingApiType::ANDROID_HCI): {
|
|
if (set_scan_rsp) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeMultiAdvtSetScanRespBuilder::Create(data, advertiser_id),
|
|
module_handler_->BindOnceOn(this, &impl::check_status_with_id<LeMultiAdvtCompleteView>, advertiser_id));
|
|
} else {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeMultiAdvtSetDataBuilder::Create(data, advertiser_id),
|
|
module_handler_->BindOnceOn(this, &impl::check_status_with_id<LeMultiAdvtCompleteView>, advertiser_id));
|
|
}
|
|
} break;
|
|
case (AdvertisingApiType::EXTENDED): {
|
|
uint16_t data_len = 0;
|
|
// check data size
|
|
for (int i = 0; i < data.size(); i++) {
|
|
if (data[i].size() > kLeMaximumFragmentLength) {
|
|
LOG_WARN("AD data len shall not greater than %d", kLeMaximumFragmentLength);
|
|
if (advertising_callbacks_ != nullptr) {
|
|
if (set_scan_rsp) {
|
|
advertising_callbacks_->OnScanResponseDataSet(
|
|
advertiser_id, AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR);
|
|
} else {
|
|
advertising_callbacks_->OnAdvertisingDataSet(
|
|
advertiser_id, AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
data_len += data[i].size();
|
|
}
|
|
|
|
if (data_len > le_maximum_advertising_data_length_) {
|
|
LOG_WARN(
|
|
"advertising data len exceeds le_maximum_advertising_data_length_ %d",
|
|
le_maximum_advertising_data_length_);
|
|
if (advertising_callbacks_ != nullptr) {
|
|
if (set_scan_rsp) {
|
|
advertising_callbacks_->OnScanResponseDataSet(
|
|
advertiser_id, AdvertisingCallback::AdvertisingStatus::DATA_TOO_LARGE);
|
|
} else {
|
|
advertising_callbacks_->OnAdvertisingDataSet(
|
|
advertiser_id, AdvertisingCallback::AdvertisingStatus::DATA_TOO_LARGE);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (data_len <= kLeMaximumFragmentLength) {
|
|
send_data_fragment(advertiser_id, set_scan_rsp, data, Operation::COMPLETE_ADVERTISEMENT);
|
|
} else {
|
|
std::vector<GapData> sub_data;
|
|
uint16_t sub_data_len = 0;
|
|
Operation operation = Operation::FIRST_FRAGMENT;
|
|
|
|
for (int i = 0; i < data.size(); i++) {
|
|
if (sub_data_len + data[i].size() > kLeMaximumFragmentLength) {
|
|
send_data_fragment(advertiser_id, set_scan_rsp, sub_data, operation);
|
|
operation = Operation::INTERMEDIATE_FRAGMENT;
|
|
sub_data_len = 0;
|
|
sub_data.clear();
|
|
}
|
|
sub_data.push_back(data[i]);
|
|
sub_data_len += data[i].size();
|
|
}
|
|
send_data_fragment(advertiser_id, set_scan_rsp, sub_data, Operation::LAST_FRAGMENT);
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
void send_data_fragment(
|
|
AdvertiserId advertiser_id, bool set_scan_rsp, std::vector<GapData> data, Operation operation) {
|
|
if (operation == Operation::COMPLETE_ADVERTISEMENT || operation == Operation::LAST_FRAGMENT) {
|
|
if (set_scan_rsp) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingScanResponseBuilder::Create(
|
|
advertiser_id, operation, kFragment_preference, data),
|
|
module_handler_->BindOnceOn(
|
|
this, &impl::check_status_with_id<LeSetExtendedAdvertisingScanResponseCompleteView>, advertiser_id));
|
|
} else {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingDataBuilder::Create(advertiser_id, operation, kFragment_preference, data),
|
|
module_handler_->BindOnceOn(
|
|
this, &impl::check_status_with_id<LeSetExtendedAdvertisingDataCompleteView>, advertiser_id));
|
|
}
|
|
} else {
|
|
// For first and intermediate fragment, do not trigger advertising_callbacks_.
|
|
if (set_scan_rsp) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingScanResponseBuilder::Create(
|
|
advertiser_id, operation, kFragment_preference, data),
|
|
module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingScanResponseCompleteView>));
|
|
} else {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingDataBuilder::Create(advertiser_id, operation, kFragment_preference, data),
|
|
module_handler_->BindOnce(impl::check_status<LeSetExtendedAdvertisingDataCompleteView>));
|
|
}
|
|
}
|
|
}
|
|
|
|
void enable_advertiser(
|
|
AdvertiserId advertiser_id, bool enable, uint16_t duration, uint8_t max_extended_advertising_events) {
|
|
EnabledSet curr_set;
|
|
curr_set.advertising_handle_ = advertiser_id;
|
|
curr_set.duration_ = duration;
|
|
curr_set.max_extended_advertising_events_ = max_extended_advertising_events;
|
|
std::vector<EnabledSet> enabled_sets = {curr_set};
|
|
Enable enable_value = enable ? Enable::ENABLED : Enable::DISABLED;
|
|
|
|
switch (advertising_api_type_) {
|
|
case (AdvertisingApiType::LEGACY): {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetAdvertisingEnableBuilder::Create(enable_value),
|
|
module_handler_->BindOnceOn(
|
|
this,
|
|
&impl::on_set_advertising_enable_complete<LeSetAdvertisingEnableCompleteView>,
|
|
enable,
|
|
enabled_sets));
|
|
} break;
|
|
case (AdvertisingApiType::ANDROID_HCI): {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeMultiAdvtSetEnableBuilder::Create(enable_value, advertiser_id),
|
|
module_handler_->BindOnceOn(
|
|
this, &impl::on_set_advertising_enable_complete<LeMultiAdvtCompleteView>, enable, enabled_sets));
|
|
} break;
|
|
case (AdvertisingApiType::EXTENDED): {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingEnableBuilder::Create(enable_value, enabled_sets),
|
|
module_handler_->BindOnceOn(
|
|
this,
|
|
&impl::on_set_extended_advertising_enable_complete<LeSetExtendedAdvertisingEnableCompleteView>,
|
|
enable,
|
|
enabled_sets));
|
|
} break;
|
|
}
|
|
|
|
if (enable) {
|
|
enabled_sets_[advertiser_id].advertising_handle_ = advertiser_id;
|
|
advertising_sets_[advertiser_id].duration = duration;
|
|
advertising_sets_[advertiser_id].max_extended_advertising_events = max_extended_advertising_events;
|
|
} else {
|
|
enabled_sets_[advertiser_id].advertising_handle_ = kInvalidHandle;
|
|
if (advertising_sets_[advertiser_id].address_rotation_alarm != nullptr) {
|
|
advertising_sets_[advertiser_id].address_rotation_alarm->Cancel();
|
|
advertising_sets_[advertiser_id].address_rotation_alarm.reset();
|
|
}
|
|
}
|
|
}
|
|
|
|
void set_periodic_parameter(
|
|
AdvertiserId advertiser_id, PeriodicAdvertisingParameters periodic_advertising_parameters) {
|
|
uint8_t include_tx_power = periodic_advertising_parameters.properties >>
|
|
PeriodicAdvertisingParameters::AdvertisingProperty::INCLUDE_TX_POWER;
|
|
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetPeriodicAdvertisingParamBuilder::Create(
|
|
advertiser_id,
|
|
periodic_advertising_parameters.min_interval,
|
|
periodic_advertising_parameters.max_interval,
|
|
include_tx_power),
|
|
module_handler_->BindOnceOn(
|
|
this, &impl::check_status_with_id<LeSetPeriodicAdvertisingParamCompleteView>, advertiser_id));
|
|
}
|
|
|
|
void set_periodic_data(AdvertiserId advertiser_id, std::vector<GapData> data) {
|
|
uint16_t data_len = 0;
|
|
// check data size
|
|
for (int i = 0; i < data.size(); i++) {
|
|
if (data[i].size() > kLeMaximumFragmentLength) {
|
|
LOG_WARN("AD data len shall not greater than %d", kLeMaximumFragmentLength);
|
|
if (advertising_callbacks_ != nullptr) {
|
|
advertising_callbacks_->OnPeriodicAdvertisingDataSet(
|
|
advertiser_id, AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR);
|
|
}
|
|
return;
|
|
}
|
|
data_len += data[i].size();
|
|
}
|
|
|
|
if (data_len > le_maximum_advertising_data_length_) {
|
|
LOG_WARN(
|
|
"advertising data len exceeds le_maximum_advertising_data_length_ %d", le_maximum_advertising_data_length_);
|
|
if (advertising_callbacks_ != nullptr) {
|
|
advertising_callbacks_->OnPeriodicAdvertisingDataSet(
|
|
advertiser_id, AdvertisingCallback::AdvertisingStatus::DATA_TOO_LARGE);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (data_len <= kLeMaximumFragmentLength) {
|
|
send_periodic_data_fragment(advertiser_id, data, Operation::COMPLETE_ADVERTISEMENT);
|
|
} else {
|
|
std::vector<GapData> sub_data;
|
|
uint16_t sub_data_len = 0;
|
|
Operation operation = Operation::FIRST_FRAGMENT;
|
|
|
|
for (int i = 0; i < data.size(); i++) {
|
|
if (sub_data_len + data[i].size() > kLeMaximumFragmentLength) {
|
|
send_periodic_data_fragment(advertiser_id, sub_data, operation);
|
|
operation = Operation::INTERMEDIATE_FRAGMENT;
|
|
sub_data_len = 0;
|
|
sub_data.clear();
|
|
}
|
|
sub_data.push_back(data[i]);
|
|
sub_data_len += data[i].size();
|
|
}
|
|
send_periodic_data_fragment(advertiser_id, sub_data, Operation::LAST_FRAGMENT);
|
|
}
|
|
}
|
|
|
|
void send_periodic_data_fragment(AdvertiserId advertiser_id, std::vector<GapData> data, Operation operation) {
|
|
if (operation == Operation::COMPLETE_ADVERTISEMENT || operation == Operation::LAST_FRAGMENT) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetPeriodicAdvertisingDataBuilder::Create(advertiser_id, operation, data),
|
|
module_handler_->BindOnceOn(
|
|
this, &impl::check_status_with_id<LeSetPeriodicAdvertisingDataCompleteView>, advertiser_id));
|
|
} else {
|
|
// For first and intermediate fragment, do not trigger advertising_callbacks_.
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetPeriodicAdvertisingDataBuilder::Create(advertiser_id, operation, data),
|
|
module_handler_->BindOnce(impl::check_status<LeSetPeriodicAdvertisingDataCompleteView>));
|
|
}
|
|
}
|
|
|
|
void enable_periodic_advertising(AdvertiserId advertiser_id, bool enable) {
|
|
Enable enable_value = enable ? Enable::ENABLED : Enable::DISABLED;
|
|
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetPeriodicAdvertisingEnableBuilder::Create(enable_value, advertiser_id),
|
|
module_handler_->BindOnceOn(
|
|
this,
|
|
&impl::on_set_periodic_advertising_enable_complete<LeSetPeriodicAdvertisingEnableCompleteView>,
|
|
enable,
|
|
advertiser_id));
|
|
}
|
|
|
|
void OnPause() override {
|
|
paused = true;
|
|
if (!advertising_sets_.empty()) {
|
|
std::vector<EnabledSet> enabled_sets = {};
|
|
for (size_t i = 0; i < enabled_sets_.size(); i++) {
|
|
EnabledSet curr_set = enabled_sets_[i];
|
|
if (enabled_sets_[i].advertising_handle_ != kInvalidHandle) {
|
|
enabled_sets.push_back(enabled_sets_[i]);
|
|
}
|
|
}
|
|
|
|
switch (advertising_api_type_) {
|
|
case (AdvertisingApiType::LEGACY): {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetAdvertisingEnableBuilder::Create(Enable::DISABLED),
|
|
module_handler_->BindOnceOn(
|
|
this,
|
|
&impl::on_set_advertising_enable_complete<LeSetAdvertisingEnableCompleteView>,
|
|
false,
|
|
enabled_sets));
|
|
} break;
|
|
case (AdvertisingApiType::ANDROID_HCI): {
|
|
for (size_t i = 0; i < enabled_sets_.size(); i++) {
|
|
uint8_t id = enabled_sets_[i].advertising_handle_;
|
|
if (id != kInvalidHandle) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeMultiAdvtSetEnableBuilder::Create(Enable::DISABLED, id),
|
|
module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
|
|
}
|
|
}
|
|
} break;
|
|
case (AdvertisingApiType::EXTENDED): {
|
|
if (enabled_sets.size() != 0) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::DISABLED, enabled_sets),
|
|
module_handler_->BindOnceOn(
|
|
this,
|
|
&impl::on_set_extended_advertising_enable_complete<LeSetExtendedAdvertisingEnableCompleteView>,
|
|
false,
|
|
enabled_sets));
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
le_address_manager_->AckPause(this);
|
|
}
|
|
|
|
void OnResume() override {
|
|
paused = false;
|
|
if (!advertising_sets_.empty()) {
|
|
std::vector<EnabledSet> enabled_sets = {};
|
|
for (size_t i = 0; i < enabled_sets_.size(); i++) {
|
|
EnabledSet curr_set = enabled_sets_[i];
|
|
if (enabled_sets_[i].advertising_handle_ != kInvalidHandle) {
|
|
enabled_sets.push_back(enabled_sets_[i]);
|
|
}
|
|
}
|
|
|
|
switch (advertising_api_type_) {
|
|
case (AdvertisingApiType::LEGACY): {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetAdvertisingEnableBuilder::Create(Enable::ENABLED),
|
|
module_handler_->BindOnceOn(
|
|
this,
|
|
&impl::on_set_advertising_enable_complete<LeSetAdvertisingEnableCompleteView>,
|
|
true,
|
|
enabled_sets));
|
|
} break;
|
|
case (AdvertisingApiType::ANDROID_HCI): {
|
|
for (size_t i = 0; i < enabled_sets_.size(); i++) {
|
|
uint8_t id = enabled_sets_[i].advertising_handle_;
|
|
if (id != kInvalidHandle) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeMultiAdvtSetEnableBuilder::Create(Enable::ENABLED, id),
|
|
module_handler_->BindOnce(impl::check_status<LeMultiAdvtCompleteView>));
|
|
}
|
|
}
|
|
} break;
|
|
case (AdvertisingApiType::EXTENDED): {
|
|
if (enabled_sets.size() != 0) {
|
|
le_advertising_interface_->EnqueueCommand(
|
|
hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::ENABLED, enabled_sets),
|
|
module_handler_->BindOnceOn(
|
|
this,
|
|
&impl::on_set_extended_advertising_enable_complete<LeSetExtendedAdvertisingEnableCompleteView>,
|
|
true,
|
|
enabled_sets));
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
le_address_manager_->AckResume(this);
|
|
}
|
|
|
|
common::Callback<void(Address, AddressType)> scan_callback_;
|
|
common::ContextualCallback<void(ErrorCode, uint16_t, hci::AddressWithType)> set_terminated_callback_{};
|
|
AdvertisingCallback* advertising_callbacks_ = nullptr;
|
|
os::Handler* registered_handler_{nullptr};
|
|
Module* module_;
|
|
os::Handler* module_handler_;
|
|
hci::HciLayer* hci_layer_;
|
|
hci::Controller* controller_;
|
|
uint16_t le_maximum_advertising_data_length_;
|
|
int8_t le_physical_channel_tx_power_ = 0;
|
|
hci::LeAdvertisingInterface* le_advertising_interface_;
|
|
std::map<AdvertiserId, Advertiser> advertising_sets_;
|
|
hci::LeAddressManager* le_address_manager_;
|
|
hci::AclManager* acl_manager_;
|
|
bool address_manager_registered = false;
|
|
bool paused = false;
|
|
|
|
std::mutex id_mutex_;
|
|
size_t num_instances_;
|
|
std::vector<hci::EnabledSet> enabled_sets_;
|
|
std::map<uint8_t, int> id_map_;
|
|
|
|
AdvertisingApiType advertising_api_type_{0};
|
|
|
|
void on_read_advertising_physical_channel_tx_power(CommandCompleteView view) {
|
|
auto complete_view = LeReadAdvertisingPhysicalChannelTxPowerCompleteView::Create(view);
|
|
ASSERT(complete_view.IsValid());
|
|
if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
|
|
LOG_INFO("Got a command complete with status %s", ErrorCodeText(complete_view.GetStatus()).c_str());
|
|
return;
|
|
}
|
|
le_physical_channel_tx_power_ = complete_view.GetTransmitPowerLevel();
|
|
}
|
|
|
|
template <class View>
|
|
void on_set_advertising_enable_complete(bool enable, std::vector<EnabledSet> enabled_sets, CommandCompleteView view) {
|
|
ASSERT(view.IsValid());
|
|
auto complete_view = View::Create(view);
|
|
ASSERT(complete_view.IsValid());
|
|
AdvertisingCallback::AdvertisingStatus advertising_status = AdvertisingCallback::AdvertisingStatus::SUCCESS;
|
|
if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
|
|
LOG_INFO("Got a command complete with status %s", ErrorCodeText(complete_view.GetStatus()).c_str());
|
|
}
|
|
|
|
if (advertising_callbacks_ == nullptr) {
|
|
return;
|
|
}
|
|
for (EnabledSet enabled_set : enabled_sets) {
|
|
bool started = advertising_sets_[enabled_set.advertising_handle_].started;
|
|
uint8_t id = enabled_set.advertising_handle_;
|
|
if (id == kInvalidHandle) {
|
|
continue;
|
|
}
|
|
|
|
if (started) {
|
|
advertising_callbacks_->OnAdvertisingEnabled(id, enable, advertising_status);
|
|
} else {
|
|
int reg_id = id_map_[id];
|
|
advertising_sets_[enabled_set.advertising_handle_].started = true;
|
|
advertising_callbacks_->OnAdvertisingSetStarted(reg_id, id, le_physical_channel_tx_power_, advertising_status);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class View>
|
|
void on_set_extended_advertising_enable_complete(
|
|
bool enable, std::vector<EnabledSet> enabled_sets, CommandCompleteView view) {
|
|
ASSERT(view.IsValid());
|
|
auto complete_view = LeSetExtendedAdvertisingEnableCompleteView::Create(view);
|
|
ASSERT(complete_view.IsValid());
|
|
AdvertisingCallback::AdvertisingStatus advertising_status = AdvertisingCallback::AdvertisingStatus::SUCCESS;
|
|
if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
|
|
LOG_INFO("Got a command complete with status %s", ErrorCodeText(complete_view.GetStatus()).c_str());
|
|
advertising_status = AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR;
|
|
}
|
|
|
|
if (advertising_callbacks_ == nullptr) {
|
|
return;
|
|
}
|
|
|
|
for (EnabledSet enabled_set : enabled_sets) {
|
|
int8_t tx_power = advertising_sets_[enabled_set.advertising_handle_].tx_power;
|
|
bool started = advertising_sets_[enabled_set.advertising_handle_].started;
|
|
uint8_t id = enabled_set.advertising_handle_;
|
|
if (id == kInvalidHandle) {
|
|
continue;
|
|
}
|
|
|
|
if (started) {
|
|
advertising_callbacks_->OnAdvertisingEnabled(id, enable, advertising_status);
|
|
} else {
|
|
int reg_id = id_map_[id];
|
|
advertising_sets_[enabled_set.advertising_handle_].started = true;
|
|
advertising_callbacks_->OnAdvertisingSetStarted(reg_id, id, tx_power, advertising_status);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class View>
|
|
void on_set_extended_advertising_parameters_complete(AdvertiserId id, CommandCompleteView view) {
|
|
ASSERT(view.IsValid());
|
|
auto complete_view = LeSetExtendedAdvertisingParametersCompleteView::Create(view);
|
|
ASSERT(complete_view.IsValid());
|
|
AdvertisingCallback::AdvertisingStatus advertising_status = AdvertisingCallback::AdvertisingStatus::SUCCESS;
|
|
if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
|
|
LOG_INFO("Got a command complete with status %s", ErrorCodeText(complete_view.GetStatus()).c_str());
|
|
advertising_status = AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR;
|
|
}
|
|
advertising_sets_[id].tx_power = complete_view.GetSelectedTxPower();
|
|
|
|
if (advertising_sets_[id].started) {
|
|
advertising_callbacks_->OnAdvertisingParametersUpdated(id, advertising_sets_[id].tx_power, advertising_status);
|
|
}
|
|
}
|
|
|
|
template <class View>
|
|
void on_set_periodic_advertising_enable_complete(bool enable, AdvertiserId id, CommandCompleteView view) {
|
|
ASSERT(view.IsValid());
|
|
auto complete_view = LeSetPeriodicAdvertisingEnableCompleteView::Create(view);
|
|
ASSERT(complete_view.IsValid());
|
|
AdvertisingCallback::AdvertisingStatus advertising_status = AdvertisingCallback::AdvertisingStatus::SUCCESS;
|
|
if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
|
|
LOG_INFO("Got a command complete with status %s", ErrorCodeText(complete_view.GetStatus()).c_str());
|
|
advertising_status = AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR;
|
|
}
|
|
|
|
if (advertising_callbacks_ == nullptr || !advertising_sets_[id].started) {
|
|
return;
|
|
}
|
|
|
|
advertising_callbacks_->OnPeriodicAdvertisingEnabled(id, enable, advertising_status);
|
|
}
|
|
|
|
template <class View>
|
|
void on_set_advertising_set_random_address_complete(
|
|
AdvertiserId advertiser_id, AddressWithType address_with_type, CommandCompleteView view) {
|
|
ASSERT(view.IsValid());
|
|
auto complete_view = LeSetExtendedAdvertisingRandomAddressCompleteView::Create(view);
|
|
ASSERT(complete_view.IsValid());
|
|
if (complete_view.GetStatus() != ErrorCode::SUCCESS) {
|
|
LOG_INFO("Got a command complete with status %s", ErrorCodeText(complete_view.GetStatus()).c_str());
|
|
} else {
|
|
LOG_INFO(
|
|
"update random address for advertising set %d : %s",
|
|
advertiser_id,
|
|
address_with_type.GetAddress().ToString().c_str());
|
|
advertising_sets_[advertiser_id].current_address = address_with_type;
|
|
}
|
|
}
|
|
|
|
template <class View>
|
|
void check_status_with_id(AdvertiserId id, CommandCompleteView view) {
|
|
ASSERT(view.IsValid());
|
|
auto status_view = View::Create(view);
|
|
ASSERT(status_view.IsValid());
|
|
if (status_view.GetStatus() != ErrorCode::SUCCESS) {
|
|
LOG_INFO(
|
|
"Got a Command complete %s, status %s",
|
|
OpCodeText(view.GetCommandOpCode()).c_str(),
|
|
ErrorCodeText(status_view.GetStatus()).c_str());
|
|
}
|
|
AdvertisingCallback::AdvertisingStatus advertising_status = AdvertisingCallback::AdvertisingStatus::SUCCESS;
|
|
if (status_view.GetStatus() != ErrorCode::SUCCESS) {
|
|
LOG_INFO("Got a command complete with status %s", ErrorCodeText(status_view.GetStatus()).c_str());
|
|
advertising_status = AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR;
|
|
}
|
|
|
|
// Do not trigger callback if the advertiser not stated yet
|
|
if (advertising_callbacks_ == nullptr || !advertising_sets_[id].started) {
|
|
return;
|
|
}
|
|
|
|
OpCode opcode = view.GetCommandOpCode();
|
|
|
|
switch (opcode) {
|
|
case OpCode::LE_SET_ADVERTISING_PARAMETERS:
|
|
advertising_callbacks_->OnAdvertisingParametersUpdated(id, le_physical_channel_tx_power_, advertising_status);
|
|
break;
|
|
case OpCode::LE_SET_ADVERTISING_DATA:
|
|
case OpCode::LE_SET_EXTENDED_ADVERTISING_DATA:
|
|
advertising_callbacks_->OnAdvertisingDataSet(id, advertising_status);
|
|
break;
|
|
case OpCode::LE_SET_SCAN_RESPONSE_DATA:
|
|
case OpCode::LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE:
|
|
advertising_callbacks_->OnScanResponseDataSet(id, advertising_status);
|
|
break;
|
|
case OpCode::LE_SET_PERIODIC_ADVERTISING_PARAM:
|
|
advertising_callbacks_->OnPeriodicAdvertisingParametersUpdated(id, advertising_status);
|
|
break;
|
|
case OpCode::LE_SET_PERIODIC_ADVERTISING_DATA:
|
|
advertising_callbacks_->OnPeriodicAdvertisingDataSet(id, advertising_status);
|
|
break;
|
|
case OpCode::LE_MULTI_ADVT: {
|
|
auto command_view = LeMultiAdvtCompleteView::Create(view);
|
|
ASSERT(command_view.IsValid());
|
|
auto sub_opcode = command_view.GetSubCmd();
|
|
switch (sub_opcode) {
|
|
case SubOcf::SET_PARAM:
|
|
advertising_callbacks_->OnAdvertisingParametersUpdated(
|
|
id, le_physical_channel_tx_power_, advertising_status);
|
|
break;
|
|
case SubOcf::SET_DATA:
|
|
advertising_callbacks_->OnAdvertisingDataSet(id, advertising_status);
|
|
break;
|
|
case SubOcf::SET_SCAN_RESP:
|
|
advertising_callbacks_->OnScanResponseDataSet(id, advertising_status);
|
|
break;
|
|
default:
|
|
LOG_WARN("Unexpected sub event type %s", SubOcfText(command_view.GetSubCmd()).c_str());
|
|
}
|
|
} break;
|
|
default:
|
|
LOG_WARN("Unexpected event type %s", OpCodeText(view.GetCommandOpCode()).c_str());
|
|
}
|
|
}
|
|
|
|
template <class View>
|
|
static void check_status(CommandCompleteView view) {
|
|
ASSERT(view.IsValid());
|
|
auto status_view = View::Create(view);
|
|
ASSERT(status_view.IsValid());
|
|
if (status_view.GetStatus() != ErrorCode::SUCCESS) {
|
|
LOG_INFO(
|
|
"Got a Command complete %s, status %s",
|
|
OpCodeText(view.GetCommandOpCode()).c_str(),
|
|
ErrorCodeText(status_view.GetStatus()).c_str());
|
|
}
|
|
}
|
|
};
|
|
|
|
LeAdvertisingManager::LeAdvertisingManager() {
|
|
pimpl_ = std::make_unique<impl>(this);
|
|
}
|
|
|
|
void LeAdvertisingManager::ListDependencies(ModuleList* list) {
|
|
list->add<hci::HciLayer>();
|
|
list->add<hci::Controller>();
|
|
list->add<hci::AclManager>();
|
|
}
|
|
|
|
void LeAdvertisingManager::Start() {
|
|
pimpl_->start(GetHandler(), GetDependency<hci::HciLayer>(), GetDependency<hci::Controller>(),
|
|
GetDependency<AclManager>());
|
|
}
|
|
|
|
void LeAdvertisingManager::Stop() {
|
|
pimpl_.reset();
|
|
}
|
|
|
|
std::string LeAdvertisingManager::ToString() const {
|
|
return "Le Advertising Manager";
|
|
}
|
|
|
|
size_t LeAdvertisingManager::GetNumberOfAdvertisingInstances() const {
|
|
return pimpl_->GetNumberOfAdvertisingInstances();
|
|
}
|
|
|
|
AdvertiserId LeAdvertisingManager::create_advertiser(
|
|
const AdvertisingConfig config,
|
|
const common::Callback<void(Address, AddressType)>& scan_callback,
|
|
const common::Callback<void(ErrorCode, uint8_t, uint8_t)>& set_terminated_callback,
|
|
os::Handler* handler) {
|
|
if (config.peer_address == Address::kEmpty) {
|
|
if (config.own_address_type == hci::OwnAddressType::RESOLVABLE_OR_PUBLIC_ADDRESS ||
|
|
config.own_address_type == hci::OwnAddressType::RESOLVABLE_OR_RANDOM_ADDRESS) {
|
|
LOG_WARN("Peer address can not be empty");
|
|
return kInvalidId;
|
|
}
|
|
if (config.advertising_type == hci::AdvertisingType::ADV_DIRECT_IND ||
|
|
config.advertising_type == hci::AdvertisingType::ADV_DIRECT_IND_LOW) {
|
|
LOG_WARN("Peer address can not be empty for directed advertising");
|
|
return kInvalidId;
|
|
}
|
|
}
|
|
AdvertiserId id = pimpl_->allocate_advertiser();
|
|
if (id == kInvalidId) {
|
|
return id;
|
|
}
|
|
GetHandler()->Post(common::BindOnce(&impl::create_advertiser, common::Unretained(pimpl_.get()), id, config,
|
|
scan_callback, set_terminated_callback, handler));
|
|
return id;
|
|
}
|
|
|
|
AdvertiserId LeAdvertisingManager::ExtendedCreateAdvertiser(
|
|
int reg_id,
|
|
const ExtendedAdvertisingConfig config,
|
|
const common::Callback<void(Address, AddressType)>& scan_callback,
|
|
const common::Callback<void(ErrorCode, uint8_t, uint8_t)>& set_terminated_callback,
|
|
uint16_t duration,
|
|
uint8_t max_extended_advertising_events,
|
|
os::Handler* handler) {
|
|
AdvertisingApiType advertising_api_type = pimpl_->get_advertising_api_type();
|
|
if (advertising_api_type != AdvertisingApiType::EXTENDED) {
|
|
return create_advertiser(config, scan_callback, set_terminated_callback, handler);
|
|
};
|
|
|
|
if (config.directed) {
|
|
if (config.peer_address == Address::kEmpty) {
|
|
LOG_INFO("Peer address can not be empty for directed advertising");
|
|
return kInvalidId;
|
|
}
|
|
}
|
|
if (config.channel_map == 0) {
|
|
LOG_INFO("At least one channel must be set in the map");
|
|
return kInvalidId;
|
|
}
|
|
if (!config.legacy_pdus) {
|
|
if (config.connectable && config.scannable) {
|
|
LOG_INFO("Extended advertising PDUs can not be connectable and scannable");
|
|
return kInvalidId;
|
|
}
|
|
if (config.high_duty_directed_connectable) {
|
|
LOG_INFO("Extended advertising PDUs can not be high duty cycle");
|
|
return kInvalidId;
|
|
}
|
|
}
|
|
if (config.interval_min > config.interval_max) {
|
|
LOG_INFO("Advertising interval: min (%hu) > max (%hu)", config.interval_min, config.interval_max);
|
|
return kInvalidId;
|
|
}
|
|
AdvertiserId id = pimpl_->allocate_advertiser();
|
|
if (id == kInvalidId) {
|
|
return id;
|
|
}
|
|
CallOn(
|
|
pimpl_.get(),
|
|
&impl::create_extended_advertiser,
|
|
reg_id,
|
|
id,
|
|
config,
|
|
scan_callback,
|
|
set_terminated_callback,
|
|
duration,
|
|
max_extended_advertising_events,
|
|
handler);
|
|
return id;
|
|
}
|
|
|
|
void LeAdvertisingManager::SetParameters(AdvertiserId advertiser_id, ExtendedAdvertisingConfig config) {
|
|
CallOn(pimpl_.get(), &impl::set_parameters, advertiser_id, config);
|
|
}
|
|
|
|
void LeAdvertisingManager::SetData(AdvertiserId advertiser_id, bool set_scan_rsp, std::vector<GapData> data) {
|
|
CallOn(pimpl_.get(), &impl::set_data, advertiser_id, set_scan_rsp, data);
|
|
}
|
|
|
|
void LeAdvertisingManager::EnableAdvertiser(
|
|
AdvertiserId advertiser_id, bool enable, uint16_t duration, uint8_t max_extended_advertising_events) {
|
|
CallOn(pimpl_.get(), &impl::enable_advertiser, advertiser_id, enable, duration, max_extended_advertising_events);
|
|
}
|
|
|
|
void LeAdvertisingManager::SetPeriodicParameters(
|
|
AdvertiserId advertiser_id, PeriodicAdvertisingParameters periodic_advertising_parameters) {
|
|
CallOn(pimpl_.get(), &impl::set_periodic_parameter, advertiser_id, periodic_advertising_parameters);
|
|
}
|
|
|
|
void LeAdvertisingManager::SetPeriodicData(AdvertiserId advertiser_id, std::vector<GapData> data) {
|
|
CallOn(pimpl_.get(), &impl::set_periodic_data, advertiser_id, data);
|
|
}
|
|
|
|
void LeAdvertisingManager::EnablePeriodicAdvertising(AdvertiserId advertiser_id, bool enable) {
|
|
CallOn(pimpl_.get(), &impl::enable_periodic_advertising, advertiser_id, enable);
|
|
}
|
|
|
|
void LeAdvertisingManager::RemoveAdvertiser(AdvertiserId advertiser_id) {
|
|
CallOn(pimpl_.get(), &impl::remove_advertiser, advertiser_id);
|
|
}
|
|
|
|
void LeAdvertisingManager::RegisterAdvertisingCallback(AdvertisingCallback* advertising_callback) {
|
|
CallOn(pimpl_.get(), &impl::register_advertising_callback, advertising_callback);
|
|
}
|
|
|
|
} // namespace hci
|
|
} // namespace bluetooth
|