/* * 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 "hci/le_advertising_manager.h" #include #include #include #include #include #include "common/init_flags.h" #include "common/strings.h" #include "hardware/ble_advertiser.h" #include "hci/acl_manager.h" #include "hci/controller.h" #include "hci/event_checkers.h" #include "hci/hci_layer.h" #include "hci/hci_packets.h" #include "hci/le_advertising_interface.h" #include "module.h" #include "os/handler.h" #include "os/log.h" #include "os/system_properties.h" #include "packet/fragmenting_inserter.h" namespace bluetooth { namespace hci { const ModuleFactory LeAdvertisingManager::Factory = ModuleFactory([]() { return new LeAdvertisingManager(); }); constexpr int kIdLocal = 0xff; // Id for advertiser not register from Java layer constexpr uint16_t kLenOfFlags = 0x03; constexpr int64_t kLeAdvertisingTxPowerMin = -127; constexpr int64_t kLeAdvertisingTxPowerMax = 20; constexpr int64_t kLeTxPathLossCompMin = -128; constexpr int64_t kLeTxPathLossCompMax = 127; // system properties const std::string kLeTxPathLossCompProperty = "bluetooth.hardware.radio.le_tx_path_loss_comp_db"; 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; // note: may not be the same as the requested_address_type, depending on the address policy AdvertiserAddressType address_type; base::OnceCallback status_callback; base::OnceCallback timeout_callback; common::Callback scan_callback; common::Callback set_terminated_callback; int8_t tx_power; uint16_t duration; uint8_t max_extended_advertising_events; bool started = false; bool is_legacy = false; bool connectable = false; bool discoverable = false; bool directed = false; bool in_use = false; bool is_periodic = false; std::unique_ptr address_rotation_alarm; }; /** * Determines the address type to use, based on the requested type and the address manager policy, * by selecting the "strictest" of the two. Strictness is defined in ascending order as * RPA -> NRPA -> Public. Thus: * (1) if the host only supports the public/static address policy, all advertisements will be public * (2) if the host supports only non-resolvable addresses, then advertisements will never use RPA * (3) if the host supports RPAs, then the requested type will always be honored */ AdvertiserAddressType GetAdvertiserAddressTypeFromRequestedTypeAndPolicy( AdvertiserAddressType requested_address_type, LeAddressManager::AddressPolicy address_policy) { switch (address_policy) { case LeAddressManager::AddressPolicy::USE_PUBLIC_ADDRESS: case LeAddressManager::AddressPolicy::USE_STATIC_ADDRESS: return AdvertiserAddressType::PUBLIC; case LeAddressManager::AddressPolicy::USE_RESOLVABLE_ADDRESS: return requested_address_type; case LeAddressManager::AddressPolicy::USE_NON_RESOLVABLE_ADDRESS: return requested_address_type == AdvertiserAddressType::RESOLVABLE_RANDOM ? AdvertiserAddressType::NONRESOLVABLE_RANDOM : requested_address_type; default: log::fatal("unreachable"); return AdvertiserAddressType::PUBLIC; } } /** * Determines the address type to use for non-connectable advertisement. * (1) if the host only supports public/static address policy, non-connectable advertisement * can use both Public and NRPA if requested. Use NRPA if RPA is requested. * (2) in other cases, based on the requested type and the address manager policy. */ AdvertiserAddressType GetAdvertiserAddressTypeNonConnectable( AdvertiserAddressType requested_address_type, LeAddressManager::AddressPolicy address_policy) { switch (address_policy) { case LeAddressManager::AddressPolicy::USE_PUBLIC_ADDRESS: case LeAddressManager::AddressPolicy::USE_STATIC_ADDRESS: return requested_address_type == AdvertiserAddressType::RESOLVABLE_RANDOM ? AdvertiserAddressType::NONRESOLVABLE_RANDOM : requested_address_type; default: return GetAdvertiserAddressTypeFromRequestedTypeAndPolicy( requested_address_type, address_policy); } } 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(); num_instances_ = controller_->GetLeNumberOfSupportedAdverisingSets(); le_advertising_interface_ = hci_layer_->GetLeAdvertisingInterface(module_handler_->BindOn(this, &LeAdvertisingManager::impl::handle_event)); hci_layer_->RegisterVendorSpecificEventHandler( hci::VseSubeventCode::BLE_STCHANGE, handler->BindOn(this, &LeAdvertisingManager::impl::multi_advertising_state_change)); if (controller_->SupportsBleExtendedAdvertising()) { advertising_api_type_ = AdvertisingApiType::EXTENDED; } else if (controller_->IsSupported(hci::OpCode::LE_MULTI_ADVT)) { advertising_api_type_ = AdvertisingApiType::ANDROID_HCI; num_instances_ = controller_->GetVendorCapabilities().max_advt_instances_; // number of LE_MULTI_ADVT start from 1 num_instances_ += 1; } else { advertising_api_type_ = AdvertisingApiType::LEGACY; int vendor_version = os::GetAndroidVendorReleaseVersion(); if (vendor_version != 0 && vendor_version <= 11 && os::IsRootCanalEnabled()) { log::info( "LeReadAdvertisingPhysicalChannelTxPower is not supported on Android R RootCanal, " "default to 0"); le_physical_channel_tx_power_ = 0; } else { hci_layer_->EnqueueCommand( LeReadAdvertisingPhysicalChannelTxPowerBuilder::Create(), handler->BindOnceOn(this, &impl::on_read_advertising_physical_channel_tx_power)); } } enabled_sets_ = std::vector(num_instances_); for (size_t i = 0; i < enabled_sets_.size(); i++) { enabled_sets_[i].advertising_handle_ = kInvalidHandle; } le_tx_path_loss_comp_ = get_tx_path_loss_compensation(); } int8_t get_tx_path_loss_compensation() { int8_t compensation = 0; auto compensation_prop = os::GetSystemProperty(kLeTxPathLossCompProperty); if (compensation_prop) { auto compensation_number = common::Int64FromString(compensation_prop.value()); if (compensation_number) { int64_t number = compensation_number.value(); if (number < kLeTxPathLossCompMin || number > kLeTxPathLossCompMax) { log::error("Invalid number for tx path loss compensation: {}", number); } else { compensation = number; } } } log::info("Tx path loss compensation: {}", compensation); return compensation; } int8_t get_tx_power_after_calibration(int8_t tx_power) { if (le_tx_path_loss_comp_ == 0) { return tx_power; } int8_t calibrated_tx_power = tx_power; int64_t number = tx_power + le_tx_path_loss_comp_; if (number < kLeAdvertisingTxPowerMin || number > kLeAdvertisingTxPowerMax) { log::error("Invalid number for calibrated tx power: {}", number); } else { calibrated_tx_power = number; } log::info("tx_power: {}, calibrated_tx_power: {}", tx_power, calibrated_tx_power); return calibrated_tx_power; } size_t GetNumberOfAdvertisingInstances() const { return num_instances_; } size_t GetNumberOfAdvertisingInstancesInUse() const { return std::count_if(advertising_sets_.begin(), advertising_sets_.end(), [](const auto& set) { return set.second.in_use; }); } int get_advertiser_reg_id(AdvertiserId advertiser_id) { return id_map_[advertiser_id]; } AdvertisingApiType get_advertising_api_type() const { return advertising_api_type_; } void register_advertising_callback(AdvertisingCallback* advertising_callback) { advertising_callbacks_ = advertising_callback; } void multi_advertising_state_change(hci::VendorSpecificEventView event) { auto view = hci::LEAdvertiseStateChangeEventView::Create(event); log::assert_that(view.IsValid(), "assert failed: view.IsValid()"); auto advertiser_id = view.GetAdvertisingInstance(); log::info( "Instance: 0x{:x} StateChangeReason: {} Handle: 0x{:x} Address: {}", advertiser_id, VseStateChangeReasonText(view.GetStateChangeReason()), view.GetConnectionHandle(), advertising_sets_[view.GetAdvertisingInstance()].current_address.ToString()); if (view.GetStateChangeReason() == VseStateChangeReason::CONNECTION_RECEIVED) { acl_manager_->OnAdvertisingSetTerminated( ErrorCode::SUCCESS, view.GetConnectionHandle(), advertiser_id, advertising_sets_[advertiser_id].current_address, advertising_sets_[advertiser_id].discoverable); enabled_sets_[advertiser_id].advertising_handle_ = kInvalidHandle; if (!advertising_sets_[advertiser_id].directed) { // TODO(250666237) calculate remaining duration and advertising events log::info("Resuming advertising, since not directed"); enable_advertiser(advertiser_id, true, 0, 0); } } } 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 {}", hci::SubeventCodeText(event.GetSubeventCode())); } } 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; } auto status = event_view.GetStatus(); log::verbose("Received LE Advertising Set Terminated with status {}", ErrorCodeText(status)); /* The Bluetooth Core 5.3 specification clearly states that this event * shall not be sent when the Host disables the advertising set. So in * case of HCI_ERROR_CANCELLED_BY_HOST, just ignore the event. */ if (status == ErrorCode::OPERATION_CANCELLED_BY_HOST) { log::warn("Unexpected advertising set terminated event status: {}", ErrorCodeText(status)); return; } uint8_t advertiser_id = event_view.GetAdvertisingHandle(); bool was_rotating_address = false; if (advertising_sets_[advertiser_id].address_rotation_alarm != nullptr) { was_rotating_address = true; 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; bool is_discoverable = advertising_sets_[event_view.GetAdvertisingHandle()].discoverable; acl_manager_->OnAdvertisingSetTerminated( status, event_view.GetConnectionHandle(), advertiser_id, advertiser_address, is_discoverable); if (status == ErrorCode::LIMIT_REACHED || status == ErrorCode::ADVERTISING_TIMEOUT) { if (id_map_[advertiser_id] == kIdLocal) { if (!advertising_sets_[advertiser_id].timeout_callback.is_null()) { std::move(advertising_sets_[advertiser_id].timeout_callback).Run((uint8_t)status); advertising_sets_[advertiser_id].timeout_callback.Reset(); } } else { advertising_callbacks_->OnAdvertisingEnabled(advertiser_id, false, (uint8_t)status); } return; } if (!advertising_sets_[advertiser_id].directed) { // TODO calculate remaining duration and advertising events if (advertising_sets_[advertiser_id].duration == 0 && advertising_sets_[advertiser_id].max_extended_advertising_events == 0) { log::info("Reenable advertising"); if (was_rotating_address) { advertising_sets_[advertiser_id].address_rotation_alarm = std::make_unique(module_handler_); advertising_sets_[advertiser_id].address_rotation_alarm->Schedule( common::BindOnce( &impl::set_advertising_set_random_address_on_timer, common::Unretained(this), advertiser_id), le_address_manager_->GetNextPrivateAddressIntervalMs()); } enable_advertiser(advertiser_id, true, 0, 0); } } } AdvertiserId allocate_advertiser() { // number of LE_MULTI_ADVT start from 1 AdvertiserId id = advertising_api_type_ == AdvertisingApiType::ANDROID_HCI ? 1 : 0; while (id < num_instances_ && advertising_sets_.count(id) != 0) { id++; } if (id == num_instances_) { log::warn("Number of max instances {} reached", (uint16_t)num_instances_); return kInvalidId; } advertising_sets_[id].in_use = true; 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_api_type_ == AdvertisingApiType::EXTENDED) { le_advertising_interface_->EnqueueCommand( hci::LeRemoveAdvertisingSetBuilder::Create(advertiser_id), module_handler_->BindOnce(check_complete)); 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); if (advertising_sets_.empty() && address_manager_registered) { le_address_manager_->Unregister(this); address_manager_registered = false; paused = false; } } /// Generates an address for the advertiser AddressWithType new_advertiser_address(AdvertiserId id) { switch (advertising_sets_[id].address_type) { case AdvertiserAddressType::PUBLIC: if (le_address_manager_->GetAddressPolicy() == LeAddressManager::AddressPolicy::USE_STATIC_ADDRESS) { return le_address_manager_->GetInitiatorAddress(); } else { return AddressWithType(controller_->GetMacAddress(), AddressType::PUBLIC_DEVICE_ADDRESS); } case AdvertiserAddressType::RESOLVABLE_RANDOM: if (advertising_api_type_ == AdvertisingApiType::LEGACY) { // we reuse the initiator address if we are a legacy advertiser using privacy, // since there's no way to use a different address return le_address_manager_->GetInitiatorAddress(); } return le_address_manager_->NewResolvableAddress(); case AdvertiserAddressType::NONRESOLVABLE_RANDOM: return le_address_manager_->NewNonResolvableAddress(); default: log::fatal("unreachable"); } } void create_advertiser( int reg_id, const AdvertisingConfig config, common::Callback scan_callback, common::Callback set_terminated_callback, os::Handler* handler) { AdvertiserId id = allocate_advertiser(); if (id == kInvalidId) { log::warn("Number of max instances reached"); start_advertising_fail(reg_id, AdvertisingCallback::AdvertisingStatus::TOO_MANY_ADVERTISERS); return; } create_advertiser_with_id(reg_id, id, config, scan_callback, set_terminated_callback, handler); } void create_advertiser_with_id( int reg_id, AdvertiserId id, const AdvertisingConfig config, common::Callback scan_callback, common::Callback set_terminated_callback, os::Handler* handler) { // check advertising data is valid before start advertising if (!check_advertising_data(config.advertisement, config.connectable && config.discoverable) || !check_advertising_data(config.scan_response, false)) { advertising_callbacks_->OnAdvertisingSetStarted( reg_id, id, le_physical_channel_tx_power_, AdvertisingCallback::AdvertisingStatus::DATA_TOO_LARGE); return; } id_map_[id] = reg_id; advertising_sets_[id].scan_callback = scan_callback; advertising_sets_[id].set_terminated_callback = set_terminated_callback; advertising_sets_[id].handler = handler; if (!address_manager_registered) { le_address_manager_->Register(this); address_manager_registered = true; } if (com::android::bluetooth::flags::nrpa_non_connectable_adv() && !config.connectable) { advertising_sets_[id].address_type = GetAdvertiserAddressTypeNonConnectable( config.requested_advertiser_address_type, le_address_manager_->GetAddressPolicy()); } else { advertising_sets_[id].address_type = GetAdvertiserAddressTypeFromRequestedTypeAndPolicy( config.requested_advertiser_address_type, le_address_manager_->GetAddressPolicy()); } advertising_sets_[id].current_address = new_advertiser_address(id); set_parameters(id, config); switch (advertising_api_type_) { case (AdvertisingApiType::LEGACY): { 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): { 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 (advertising_sets_[id].address_type != AdvertiserAddressType::PUBLIC) { le_advertising_interface_->EnqueueCommand( hci::LeMultiAdvtSetRandomAddrBuilder::Create( advertising_sets_[id].current_address.GetAddress(), id), module_handler_->BindOnce(check_complete)); } 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 start_advertising( AdvertiserId id, const AdvertisingConfig config, uint16_t duration, base::OnceCallback status_callback, base::OnceCallback timeout_callback, const common::Callback scan_callback, const common::Callback set_terminated_callback, os::Handler* handler) { advertising_sets_[id].status_callback = std::move(status_callback); advertising_sets_[id].timeout_callback = std::move(timeout_callback); // legacy start_advertising use default jni client id create_extended_advertiser_with_id( kAdvertiserClientIdJni, kIdLocal, id, config, scan_callback, set_terminated_callback, duration, 0, handler); } void create_extended_advertiser( uint8_t client_id, int reg_id, const AdvertisingConfig config, common::Callback scan_callback, common::Callback set_terminated_callback, uint16_t duration, uint8_t max_ext_adv_events, os::Handler* handler) { AdvertiserId id = allocate_advertiser(); if (id == kInvalidId) { log::warn("Number of max instances reached"); start_advertising_fail(reg_id, AdvertisingCallback::AdvertisingStatus::TOO_MANY_ADVERTISERS); return; } create_extended_advertiser_with_id( client_id, reg_id, id, config, scan_callback, set_terminated_callback, duration, max_ext_adv_events, handler); } void create_extended_advertiser_with_id( uint8_t client_id, int reg_id, AdvertiserId id, const AdvertisingConfig config, common::Callback scan_callback, common::Callback 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_with_id( reg_id, id, config, scan_callback, set_terminated_callback, handler); return; } // check extended advertising data is valid before start advertising if (!check_extended_advertising_data( config.advertisement, config.connectable && config.discoverable) || !check_extended_advertising_data(config.scan_response, false)) { advertising_callbacks_->OnAdvertisingSetStarted( reg_id, id, le_physical_channel_tx_power_, AdvertisingCallback::AdvertisingStatus::DATA_TOO_LARGE); return; } if (!address_manager_registered) { le_address_manager_->Register(this); address_manager_registered = true; } 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; if (com::android::bluetooth::flags::nrpa_non_connectable_adv() && !config.connectable) { advertising_sets_[id].address_type = GetAdvertiserAddressTypeNonConnectable( config.requested_advertiser_address_type, le_address_manager_->GetAddressPolicy()); } else { advertising_sets_[id].address_type = GetAdvertiserAddressTypeFromRequestedTypeAndPolicy( config.requested_advertiser_address_type, le_address_manager_->GetAddressPolicy()); } advertising_sets_[id].current_address = new_advertiser_address(id); set_parameters(id, config); if (advertising_sets_[id].current_address.GetAddressType() != AddressType::PUBLIC_DEVICE_ADDRESS) { // if we aren't using the public address type at the HCI level, we need to set the random // address le_advertising_interface_->EnqueueCommand( hci::LeSetAdvertisingSetRandomAddressBuilder::Create( id, advertising_sets_[id].current_address.GetAddress()), module_handler_->BindOnceOn( this, &impl::on_set_advertising_set_random_address_complete< LeSetAdvertisingSetRandomAddressCompleteView>, id, advertising_sets_[id].current_address)); bool leaudio_requested_nrpa = false; if (client_id == kAdvertiserClientIdLeAudio && advertising_sets_[id].address_type == AdvertiserAddressType::NONRESOLVABLE_RANDOM) { log::info( "Advertiser started by le audio client with address type: {}", advertising_sets_[id].address_type); leaudio_requested_nrpa = true; } // but we only rotate if the AdvertiserAddressType is non-public // or non-rpa requested by leaudio(since static random addresses don't rotate) if (advertising_sets_[id].address_type != AdvertiserAddressType::PUBLIC && !leaudio_requested_nrpa) { // start timer for random address advertising_sets_[id].address_rotation_alarm = std::make_unique(module_handler_); advertising_sets_[id].address_rotation_alarm->Schedule( common::BindOnce( &impl::set_advertising_set_random_address_on_timer, common::Unretained(this), id), le_address_manager_->GetNextPrivateAddressIntervalMs()); } } 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, config.periodic_advertising_parameters.enable, config.periodic_advertising_parameters.include_adi); } 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 enabled_sets = {curr_set}; enabled_sets_[id] = curr_set; } } void stop_advertising(AdvertiserId advertiser_id) { auto advertising_iter = advertising_sets_.find(advertiser_id); if (advertising_iter == advertising_sets_.end()) { log::info("Unknown advertising set {}", advertiser_id); return; } EnabledSet curr_set; curr_set.advertising_handle_ = advertiser_id; std::vector 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(check_complete)); break; case (AdvertisingApiType::ANDROID_HCI): le_advertising_interface_->EnqueueCommand( hci::LeMultiAdvtSetEnableBuilder::Create(Enable::DISABLED, advertiser_id), module_handler_->BindOnce(check_complete)); break; case (AdvertisingApiType::EXTENDED): { le_advertising_interface_->EnqueueCommand( hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::DISABLED, enabled_vector), module_handler_->BindOnce(check_complete)); bool is_periodic = advertising_iter->second.is_periodic; log::debug("advertiser_id: {} is_periodic: {}", advertiser_id, is_periodic); // Only set periodic advertising if supported. if (is_periodic && controller_->SupportsBlePeriodicAdvertising()) { le_advertising_interface_->EnqueueCommand( hci::LeSetPeriodicAdvertisingEnableBuilder::Create(false, false, advertiser_id), module_handler_->BindOnce( check_complete)); } } break; } std::unique_lock lock(id_mutex_); enabled_sets_[advertiser_id].advertising_handle_ = kInvalidHandle; } void rotate_advertiser_address(AdvertiserId advertiser_id) { if (advertising_api_type_ == AdvertisingApiType::EXTENDED) { AddressWithType address_with_type = new_advertiser_address(advertiser_id); le_advertising_interface_->EnqueueCommand( hci::LeSetAdvertisingSetRandomAddressBuilder::Create(advertiser_id, address_with_type.GetAddress()), module_handler_->BindOnceOn( this, &impl::on_set_advertising_set_random_address_complete, advertiser_id, address_with_type)); } } void set_advertising_set_random_address_on_timer(AdvertiserId advertiser_id) { // This function should only be trigger by enabled advertising set or IRK rotation 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 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(check_complete)); } rotate_advertiser_address(advertiser_id); // If we are paused, we will be enabled in OnResume(), so don't resume now. // Note that OnResume() can never re-enable us while we are changing our address, since the // DISABLED and ENABLED commands are enqueued synchronously, so OnResume() doesn't need an // analogous check. if (advertising_sets_[advertiser_id].connectable && !paused) { le_advertising_interface_->EnqueueCommand( hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::ENABLED, enabled_sets), module_handler_->BindOnce(check_complete)); } advertising_sets_[advertiser_id].address_rotation_alarm->Schedule( common::BindOnce(&impl::set_advertising_set_random_address_on_timer, common::Unretained(this), advertiser_id), le_address_manager_->GetNextPrivateAddressIntervalMs()); } void register_advertiser( common::ContextualOnceCallback callback) { AdvertiserId id = allocate_advertiser(); if (id == kInvalidId) { callback(kInvalidId, AdvertisingCallback::AdvertisingStatus::TOO_MANY_ADVERTISERS); } else { callback(id, AdvertisingCallback::AdvertisingStatus::SUCCESS); } } void get_own_address(AdvertiserId advertiser_id) { if (advertising_sets_.find(advertiser_id) == advertising_sets_.end()) { log::info("Unknown advertising id {}", advertiser_id); return; } auto current_address = advertising_sets_[advertiser_id].current_address; advertising_callbacks_->OnOwnAddressRead( advertiser_id, static_cast(current_address.GetAddressType()), current_address.GetAddress()); } void set_parameters(AdvertiserId advertiser_id, AdvertisingConfig config) { config.tx_power = get_tx_power_after_calibration(static_cast(config.tx_power)); advertising_sets_[advertiser_id].is_legacy = config.legacy_pdus; advertising_sets_[advertiser_id].connectable = config.connectable; advertising_sets_[advertiser_id].discoverable = config.discoverable; advertising_sets_[advertiser_id].tx_power = config.tx_power; advertising_sets_[advertiser_id].directed = config.directed; advertising_sets_[advertiser_id].is_periodic = config.periodic_advertising_parameters.enable; // based on logic in new_advertiser_address auto own_address_type = static_cast( advertising_sets_[advertiser_id].current_address.GetAddressType()); switch (advertising_api_type_) { case (AdvertisingApiType::LEGACY): { le_advertising_interface_->EnqueueCommand( hci::LeSetAdvertisingParametersBuilder::Create( config.interval_min, config.interval_max, config.advertising_type, 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, true, advertiser_id)); } break; case (AdvertisingApiType::ANDROID_HCI): { le_advertising_interface_->EnqueueCommand( hci::LeMultiAdvtParamBuilder::Create( config.interval_min, config.interval_max, config.advertising_type, own_address_type, advertising_sets_[advertiser_id].current_address.GetAddress(), 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, true, 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) { LegacyAdvertisingEventProperties legacy_properties = LegacyAdvertisingEventProperties::ADV_IND; if (config.connectable && config.directed) { if (config.high_duty_directed_connectable) { legacy_properties = LegacyAdvertisingEventProperties::ADV_DIRECT_IND_HIGH; } else { legacy_properties = LegacyAdvertisingEventProperties::ADV_DIRECT_IND_LOW; } } if (config.scannable && !config.connectable) { legacy_properties = LegacyAdvertisingEventProperties::ADV_SCAN_IND; } if (!config.scannable && !config.connectable) { legacy_properties = LegacyAdvertisingEventProperties::ADV_NONCONN_IND; } le_advertising_interface_->EnqueueCommand( LeSetExtendedAdvertisingParametersLegacyBuilder::Create( advertiser_id, legacy_properties, config.interval_min, config.interval_max, config.channel_map, 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 { AdvertisingEventProperties extended_properties; extended_properties.connectable_ = config.connectable; extended_properties.scannable_ = config.scannable; extended_properties.directed_ = config.directed; extended_properties.high_duty_cycle_ = config.high_duty_directed_connectable; extended_properties.legacy_ = false; extended_properties.anonymous_ = config.anonymous; extended_properties.tx_power_ = config.include_tx_power; le_advertising_interface_->EnqueueCommand( hci::LeSetExtendedAdvertisingParametersBuilder::Create( advertiser_id, extended_properties, config.interval_min, config.interval_max, config.channel_map, 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; } } bool data_has_flags(std::vector data) { for (auto& gap_data : data) { if (gap_data.data_type_ == GapDataType::FLAGS) { return true; } } return false; } bool check_advertising_data(std::vector data, bool include_flag) { uint16_t data_len = 0; // check data size for (size_t i = 0; i < data.size(); i++) { data_len += data[i].size(); } // The Flags data type shall be included when any of the Flag bits are non-zero and the // advertising packet is connectable and discoverable. It will be added by set_data() function, // we should count it here. if (include_flag && !data_has_flags(data)) { data_len += kLenOfFlags; } if (data_len > le_maximum_advertising_data_length_) { log::warn( "advertising data len {} exceeds le_maximum_advertising_data_length_ {}", data_len, le_maximum_advertising_data_length_); return false; } return true; }; bool check_extended_advertising_data(std::vector data, bool include_flag) { uint16_t data_len = 0; uint16_t data_limit = com::android::bluetooth::flags::divide_long_single_gap_data() ? kLeMaximumGapDataLength : kLeMaximumFragmentLength; // check data size for (size_t i = 0; i < data.size(); i++) { if (data[i].size() > data_limit) { log::warn("AD data len shall not greater than {}", data_limit); return false; } data_len += data[i].size(); } // The Flags data type shall be included when any of the Flag bits are non-zero and the // advertising packet is connectable and discoverable. It will be added by set_data() function, // we should count it here. if (include_flag && !data_has_flags(data)) { data_len += kLenOfFlags; } if (data_len > le_maximum_advertising_data_length_) { log::warn( "advertising data len {} exceeds le_maximum_advertising_data_length_ {}", data_len, le_maximum_advertising_data_length_); return false; } return true; }; void set_data(AdvertiserId advertiser_id, bool set_scan_rsp, std::vector data) { // The Flags data type shall be included when any of the Flag bits are non-zero and the // advertising packet is connectable and discoverable. if (!set_scan_rsp && advertising_sets_[advertiser_id].connectable && advertising_sets_[advertiser_id].discoverable && !data_has_flags(data)) { GapData gap_data; gap_data.data_type_ = GapDataType::FLAGS; if (advertising_sets_[advertiser_id].duration == 0) { gap_data.data_.push_back(static_cast(AdvertisingFlag::LE_GENERAL_DISCOVERABLE)); } else { gap_data.data_.push_back(static_cast(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; } } if (advertising_api_type_ != AdvertisingApiType::EXTENDED && !check_advertising_data(data, false)) { 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; } 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, true, advertiser_id)); } else { le_advertising_interface_->EnqueueCommand( hci::LeSetAdvertisingDataBuilder::Create(data), module_handler_->BindOnceOn( this, &impl::check_status_with_id, true, 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, true, advertiser_id)); } else { le_advertising_interface_->EnqueueCommand( hci::LeMultiAdvtSetDataBuilder::Create(data, advertiser_id), module_handler_->BindOnceOn( this, &impl::check_status_with_id, true, advertiser_id)); } } break; case (AdvertisingApiType::EXTENDED): { uint16_t data_len = 0; bool divide_gap_flag = com::android::bluetooth::flags::divide_long_single_gap_data(); // check data size for (size_t i = 0; i < data.size(); i++) { uint16_t data_limit = divide_gap_flag ? kLeMaximumGapDataLength : kLeMaximumFragmentLength; if (data[i].size() > data_limit) { log::warn("AD data len shall not greater than {}", data_limit); 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(); } int maxDataLength = (com::android::bluetooth::flags::ble_check_data_length_on_legacy_advertising() && advertising_sets_[advertiser_id].is_legacy) ? kLeMaximumLegacyAdvertisingDataLength : le_maximum_advertising_data_length_; if (data_len > maxDataLength) { log::warn("advertising data len {} exceeds maxDataLength {}", data_len, maxDataLength); 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 sub_data; uint16_t sub_data_len = 0; Operation operation = Operation::FIRST_FRAGMENT; if (divide_gap_flag) { std::vector> fragments; packet::FragmentingInserter it( kLeMaximumFragmentLength, std::back_insert_iterator(fragments)); for (auto gap_data : data) { gap_data.Serialize(it); } it.finalize(); for (size_t i = 0; i < fragments.size(); i++) { send_data_fragment_with_raw_builder( advertiser_id, set_scan_rsp, std::move(fragments[i]), (i == fragments.size() - 1) ? Operation::LAST_FRAGMENT : operation); operation = Operation::INTERMEDIATE_FRAGMENT; } } else { for (size_t 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 data, Operation operation) { if (com::android::bluetooth::flags::divide_long_single_gap_data()) { // For first and intermediate fragment, do not trigger advertising_callbacks_. bool send_callback = (operation == Operation::COMPLETE_ADVERTISEMENT || operation == Operation::LAST_FRAGMENT); if (set_scan_rsp) { le_advertising_interface_->EnqueueCommand( hci::LeSetExtendedScanResponseDataBuilder::Create( advertiser_id, operation, kFragment_preference, data), module_handler_->BindOnceOn( this, &impl::check_status_with_id, send_callback, 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, send_callback, advertiser_id)); } } else { if (operation == Operation::COMPLETE_ADVERTISEMENT || operation == Operation::LAST_FRAGMENT) { if (set_scan_rsp) { le_advertising_interface_->EnqueueCommand( hci::LeSetExtendedScanResponseDataBuilder::Create( advertiser_id, operation, kFragment_preference, data), module_handler_->BindOnceOn( this, &impl::check_status_with_id, true, 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, true, advertiser_id)); } } else { // For first and intermediate fragment, do not trigger advertising_callbacks_. if (set_scan_rsp) { le_advertising_interface_->EnqueueCommand( hci::LeSetExtendedScanResponseDataBuilder::Create( advertiser_id, operation, kFragment_preference, data), module_handler_->BindOnce(check_complete)); } else { le_advertising_interface_->EnqueueCommand( hci::LeSetExtendedAdvertisingDataBuilder::Create( advertiser_id, operation, kFragment_preference, data), module_handler_->BindOnce(check_complete)); } } } } void send_data_fragment_with_raw_builder( AdvertiserId advertiser_id, bool set_scan_rsp, std::unique_ptr data, Operation operation) { // For first and intermediate fragment, do not trigger advertising_callbacks_. bool send_callback = (operation == Operation::COMPLETE_ADVERTISEMENT || operation == Operation::LAST_FRAGMENT); if (set_scan_rsp) { le_advertising_interface_->EnqueueCommand( hci::LeSetExtendedScanResponseDataRawBuilder::Create( advertiser_id, operation, kFragment_preference, std::move(data)), module_handler_->BindOnceOn( this, &impl::check_status_with_id, send_callback, advertiser_id)); } else { le_advertising_interface_->EnqueueCommand( hci::LeSetExtendedAdvertisingDataRawBuilder::Create( advertiser_id, operation, kFragment_preference, std::move(data)), module_handler_->BindOnceOn( this, &impl::check_status_with_id, send_callback, advertiser_id)); } } 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 enabled_sets = {curr_set}; Enable enable_value = enable ? Enable::ENABLED : Enable::DISABLED; if (!advertising_sets_.count(advertiser_id)) { log::warn("No advertising set with key: {}", advertiser_id); return; } 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, enable, enabled_sets, true /* trigger callbacks */)); } 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, enable, enabled_sets, true /* trigger callbacks */)); } 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, true /* trigger callbacks */)); } break; } if (enable) { enabled_sets_[advertiser_id].advertising_handle_ = advertiser_id; if (advertising_api_type_ == AdvertisingApiType::EXTENDED) { enabled_sets_[advertiser_id].duration_ = duration; enabled_sets_[advertiser_id].max_extended_advertising_events_ = max_extended_advertising_events; } 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::LeSetPeriodicAdvertisingParametersBuilder::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, true, advertiser_id)); } void set_periodic_data(AdvertiserId advertiser_id, std::vector data) { uint16_t data_len = 0; bool divide_gap_flag = com::android::bluetooth::flags::divide_long_single_gap_data(); // check data size for (size_t i = 0; i < data.size(); i++) { uint16_t data_limit = divide_gap_flag ? kLeMaximumGapDataLength : kLeMaximumFragmentLength; if (data[i].size() > data_limit) { log::warn("AD data len shall not greater than {}", data_limit); 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_ {}", le_maximum_advertising_data_length_); if (advertising_callbacks_ != nullptr) { advertising_callbacks_->OnPeriodicAdvertisingDataSet( advertiser_id, AdvertisingCallback::AdvertisingStatus::DATA_TOO_LARGE); } return; } uint16_t data_fragment_limit = divide_gap_flag ? kLeMaximumPeriodicDataFragmentLength : kLeMaximumFragmentLength; if (data_len <= data_fragment_limit) { send_periodic_data_fragment(advertiser_id, data, Operation::COMPLETE_ADVERTISEMENT); } else { std::vector sub_data; uint16_t sub_data_len = 0; Operation operation = Operation::FIRST_FRAGMENT; if (divide_gap_flag) { std::vector> fragments; packet::FragmentingInserter it( kLeMaximumPeriodicDataFragmentLength, std::back_insert_iterator(fragments)); for (auto gap_data : data) { gap_data.Serialize(it); } it.finalize(); for (size_t i = 0; i < fragments.size(); i++) { send_periodic_data_fragment_with_raw_builder( advertiser_id, std::move(fragments[i]), (i == fragments.size() - 1) ? Operation::LAST_FRAGMENT : operation); operation = Operation::INTERMEDIATE_FRAGMENT; } } else { for (size_t 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 data, Operation operation) { if (com::android::bluetooth::flags::divide_long_single_gap_data()) { // For first and intermediate fragment, do not trigger advertising_callbacks_. bool send_callback = (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, send_callback, advertiser_id)); } else { 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, true, 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(check_complete)); } } } void send_periodic_data_fragment_with_raw_builder( AdvertiserId advertiser_id, std::unique_ptr data, Operation operation) { // For first and intermediate fragment, do not trigger advertising_callbacks_. bool send_callback = (operation == Operation::COMPLETE_ADVERTISEMENT || operation == Operation::LAST_FRAGMENT); le_advertising_interface_->EnqueueCommand( hci::LeSetPeriodicAdvertisingDataRawBuilder::Create( advertiser_id, operation, std::move(data)), module_handler_->BindOnceOn( this, &impl::check_status_with_id, send_callback, advertiser_id)); } void enable_periodic_advertising(AdvertiserId advertiser_id, bool enable, bool include_adi) { if (!controller_->SupportsBlePeriodicAdvertising()) { return; } if (include_adi && !controller_->SupportsBlePeriodicAdvertisingAdi()) { include_adi = false; } le_advertising_interface_->EnqueueCommand( hci::LeSetPeriodicAdvertisingEnableBuilder::Create(enable, include_adi, advertiser_id), module_handler_->BindOnceOn( this, &impl::on_set_periodic_advertising_enable_complete, enable, advertiser_id)); } void OnPause() override { if (!address_manager_registered) { log::warn("Unregistered!"); return; } paused = true; if (!advertising_sets_.empty()) { std::vector 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_->BindOnce(check_complete)); } 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(check_complete)); } } } break; case (AdvertisingApiType::EXTENDED): { if (enabled_sets.size() != 0) { le_advertising_interface_->EnqueueCommand( hci::LeSetExtendedAdvertisingEnableBuilder::Create(Enable::DISABLED, enabled_sets), module_handler_->BindOnce( check_complete)); } } break; } } le_address_manager_->AckPause(this); } void OnResume() override { if (!address_manager_registered) { log::warn("Unregistered!"); return; } paused = false; if (!advertising_sets_.empty()) { std::vector 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, true, enabled_sets, false /* trigger_callbacks */)); } 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_->BindOnceOn( this, &impl::on_set_advertising_enable_complete, true, enabled_sets, false /* trigger_callbacks */)); } } } 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, false /* trigger_callbacks */)); } } break; } } le_address_manager_->AckResume(this); } // Note: this needs to be synchronous (i.e. NOT on a handler) for two reasons: // 1. For parity with OnPause() and OnResume() // 2. If we don't enqueue our HCI commands SYNCHRONOUSLY, then it is possible that we OnResume() in addressManager // before our commands complete. So then our commands reach the HCI layer *after* the resume commands from address // manager, which is racey (even if it might not matter). // // If you are a future developer making this asynchronous, you need to add some kind of ->AckIRKChange() method to the // address manager so we can defer resumption to after this completes. void NotifyOnIRKChange() override { for (size_t i = 0; i < enabled_sets_.size(); i++) { if (enabled_sets_[i].advertising_handle_ != kInvalidHandle) { rotate_advertiser_address(i); } } } common::Callback scan_callback_; common::ContextualCallback 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; int8_t le_tx_path_loss_comp_ = 0; hci::LeAdvertisingInterface* le_advertising_interface_; std::map 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 enabled_sets_; // map to mapping the id from java layer and advertier id std::map id_map_; AdvertisingApiType advertising_api_type_{0}; void on_read_advertising_physical_channel_tx_power(CommandCompleteView view) { auto complete_view = LeReadAdvertisingPhysicalChannelTxPowerCompleteView::Create(view); if (!complete_view.IsValid()) { auto payload = view.GetPayload(); if (payload.size() == 1 && payload[0] == static_cast(ErrorCode::UNKNOWN_HCI_COMMAND)) { log::info("Unknown command, not setting tx power"); return; } } log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()"); if (complete_view.GetStatus() != ErrorCode::SUCCESS) { log::info("Got a command complete with status {}", ErrorCodeText(complete_view.GetStatus())); return; } le_physical_channel_tx_power_ = complete_view.GetTransmitPowerLevel(); } template void on_set_advertising_enable_complete( bool enable, std::vector enabled_sets, bool trigger_callbacks, CommandCompleteView view) { log::assert_that(view.IsValid(), "assert failed: view.IsValid()"); auto complete_view = View::Create(view); log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()"); AdvertisingCallback::AdvertisingStatus advertising_status = AdvertisingCallback::AdvertisingStatus::SUCCESS; if (complete_view.GetStatus() != ErrorCode::SUCCESS) { log::info("Got a command complete with status {}", ErrorCodeText(complete_view.GetStatus())); } 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; } int reg_id = id_map_[id]; if (reg_id == kIdLocal) { if (!advertising_sets_[enabled_set.advertising_handle_].status_callback.is_null()) { std::move(advertising_sets_[enabled_set.advertising_handle_].status_callback).Run(advertising_status); advertising_sets_[enabled_set.advertising_handle_].status_callback.Reset(); } continue; } if (started) { if (trigger_callbacks) { advertising_callbacks_->OnAdvertisingEnabled(id, enable, advertising_status); } } else { advertising_sets_[enabled_set.advertising_handle_].started = true; advertising_callbacks_->OnAdvertisingSetStarted(reg_id, id, le_physical_channel_tx_power_, advertising_status); } } } template void on_set_extended_advertising_enable_complete( bool enable, std::vector enabled_sets, bool trigger_callbacks, CommandCompleteView view) { log::assert_that(view.IsValid(), "assert failed: view.IsValid()"); auto complete_view = LeSetExtendedAdvertisingEnableCompleteView::Create(view); log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()"); AdvertisingCallback::AdvertisingStatus advertising_status = AdvertisingCallback::AdvertisingStatus::SUCCESS; if (complete_view.GetStatus() != ErrorCode::SUCCESS) { log::info("Got a command complete with status {}", ErrorCodeText(complete_view.GetStatus())); 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; } int reg_id = id_map_[id]; if (reg_id == kIdLocal) { if (!advertising_sets_[enabled_set.advertising_handle_].status_callback.is_null()) { std::move(advertising_sets_[enabled_set.advertising_handle_].status_callback).Run(advertising_status); advertising_sets_[enabled_set.advertising_handle_].status_callback.Reset(); } continue; } if (started) { if (trigger_callbacks) { advertising_callbacks_->OnAdvertisingEnabled(id, enable, advertising_status); } } else { advertising_sets_[enabled_set.advertising_handle_].started = true; advertising_callbacks_->OnAdvertisingSetStarted(reg_id, id, tx_power, advertising_status); } } } template void on_set_extended_advertising_parameters_complete(AdvertiserId id, CommandCompleteView view) { log::assert_that(view.IsValid(), "assert failed: view.IsValid()"); auto complete_view = LeSetExtendedAdvertisingParametersCompleteView::Create(view); log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()"); AdvertisingCallback::AdvertisingStatus advertising_status = AdvertisingCallback::AdvertisingStatus::SUCCESS; if (complete_view.GetStatus() != ErrorCode::SUCCESS) { log::info("Got a command complete with status {}", ErrorCodeText(complete_view.GetStatus())); advertising_status = AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR; } advertising_sets_[id].tx_power = complete_view.GetSelectedTxPower(); if (advertising_sets_[id].started && id_map_[id] != kIdLocal) { advertising_callbacks_->OnAdvertisingParametersUpdated(id, advertising_sets_[id].tx_power, advertising_status); } } template void on_set_periodic_advertising_enable_complete(bool enable, AdvertiserId id, CommandCompleteView view) { log::assert_that(view.IsValid(), "assert failed: view.IsValid()"); auto complete_view = LeSetPeriodicAdvertisingEnableCompleteView::Create(view); log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()"); AdvertisingCallback::AdvertisingStatus advertising_status = AdvertisingCallback::AdvertisingStatus::SUCCESS; if (complete_view.GetStatus() != ErrorCode::SUCCESS) { log::info("Got a command complete with status {}", ErrorCodeText(complete_view.GetStatus())); advertising_status = AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR; } if (advertising_callbacks_ == nullptr || !advertising_sets_[id].started || id_map_[id] == kIdLocal) { return; } advertising_callbacks_->OnPeriodicAdvertisingEnabled(id, enable, advertising_status); } template void on_set_advertising_set_random_address_complete( AdvertiserId advertiser_id, AddressWithType address_with_type, CommandCompleteView view) { log::assert_that(view.IsValid(), "assert failed: view.IsValid()"); auto complete_view = LeSetAdvertisingSetRandomAddressCompleteView::Create(view); log::assert_that(complete_view.IsValid(), "assert failed: complete_view.IsValid()"); if (complete_view.GetStatus() != ErrorCode::SUCCESS) { log::error("Got a command complete with status {}", ErrorCodeText(complete_view.GetStatus())); } else { log::info( "update random address for advertising set {} : {}", advertiser_id, address_with_type.GetAddress()); advertising_sets_[advertiser_id].current_address = address_with_type; } } template void check_status_with_id(bool send_callback, AdvertiserId id, CommandCompleteView view) { log::assert_that(view.IsValid(), "assert failed: view.IsValid()"); auto status_view = View::Create(view); log::assert_that(status_view.IsValid(), "assert failed: status_view.IsValid()"); if (status_view.GetStatus() != ErrorCode::SUCCESS) { log::info( "Got a Command complete {}, status {}", OpCodeText(view.GetCommandOpCode()), ErrorCodeText(status_view.GetStatus())); } AdvertisingCallback::AdvertisingStatus advertising_status = AdvertisingCallback::AdvertisingStatus::SUCCESS; if (status_view.GetStatus() != ErrorCode::SUCCESS) { log::info("Got a command complete with status {}", ErrorCodeText(status_view.GetStatus())); advertising_status = AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR; } // Do not trigger callback if the advertiser not stated yet, or the advertiser is not register // from Java layer if (advertising_callbacks_ == nullptr || !advertising_sets_[id].started || id_map_[id] == kIdLocal) { return; } if (com::android::bluetooth::flags::divide_long_single_gap_data()) { // Do not trigger callback if send_callback is false if (!send_callback) { 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_SCAN_RESPONSE_DATA: advertising_callbacks_->OnScanResponseDataSet(id, advertising_status); break; case OpCode::LE_SET_PERIODIC_ADVERTISING_PARAMETERS: 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); log::assert_that(command_view.IsValid(), "assert failed: 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 {}", SubOcfText(command_view.GetSubCmd())); } } break; default: log::warn("Unexpected event type {}", OpCodeText(view.GetCommandOpCode())); } } void start_advertising_fail(int reg_id, AdvertisingCallback::AdvertisingStatus status) { log::assert_that( status != AdvertisingCallback::AdvertisingStatus::SUCCESS, "assert failed: status != AdvertisingCallback::AdvertisingStatus::SUCCESS"); advertising_callbacks_->OnAdvertisingSetStarted(reg_id, kInvalidId, 0, status); } }; LeAdvertisingManager::LeAdvertisingManager() { pimpl_ = std::make_unique(this); } void LeAdvertisingManager::ListDependencies(ModuleList* list) const { list->add(); list->add(); list->add(); } void LeAdvertisingManager::Start() { pimpl_->start( GetHandler(), GetDependency(), GetDependency(), GetDependency()); } void LeAdvertisingManager::Stop() { pimpl_.reset(); } std::string LeAdvertisingManager::ToString() const { return "Le Advertising Manager"; } size_t LeAdvertisingManager::GetNumberOfAdvertisingInstances() const { return pimpl_->GetNumberOfAdvertisingInstances(); } size_t LeAdvertisingManager::GetNumberOfAdvertisingInstancesInUse() const { return pimpl_->GetNumberOfAdvertisingInstancesInUse(); } int LeAdvertisingManager::GetAdvertiserRegId(AdvertiserId advertiser_id) { return pimpl_->get_advertiser_reg_id(advertiser_id); } void LeAdvertisingManager::ExtendedCreateAdvertiser( uint8_t client_id, int reg_id, const AdvertisingConfig config, common::Callback scan_callback, common::Callback 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) { if (config.peer_address == Address::kEmpty) { if (config.advertising_type == hci::AdvertisingType::ADV_DIRECT_IND_HIGH || config.advertising_type == hci::AdvertisingType::ADV_DIRECT_IND_LOW) { log::warn("Peer address can not be empty for directed advertising"); CallOn( pimpl_.get(), &impl::start_advertising_fail, reg_id, AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR); return; } } GetHandler()->Post(common::BindOnce( &impl::create_advertiser, common::Unretained(pimpl_.get()), reg_id, config, scan_callback, set_terminated_callback, handler)); return; }; if (config.directed) { if (config.peer_address == Address::kEmpty) { log::info("Peer address can not be empty for directed advertising"); CallOn( pimpl_.get(), &impl::start_advertising_fail, reg_id, AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR); return; } } if (config.channel_map == 0) { log::info("At least one channel must be set in the map"); CallOn(pimpl_.get(), &impl::start_advertising_fail, reg_id, AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR); return; } if (!config.legacy_pdus) { if (config.connectable && config.scannable) { log::info("Extended advertising PDUs can not be connectable and scannable"); CallOn( pimpl_.get(), &impl::start_advertising_fail, reg_id, AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR); return; } if (config.high_duty_directed_connectable) { log::info("Extended advertising PDUs can not be high duty cycle"); CallOn( pimpl_.get(), &impl::start_advertising_fail, reg_id, AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR); return; } } if (config.interval_min > config.interval_max) { log::info( "Advertising interval: min ({}) > max ({})", config.interval_min, config.interval_max); CallOn(pimpl_.get(), &impl::start_advertising_fail, reg_id, AdvertisingCallback::AdvertisingStatus::INTERNAL_ERROR); return; } CallOn( pimpl_.get(), &impl::create_extended_advertiser, client_id, reg_id, config, scan_callback, set_terminated_callback, duration, max_extended_advertising_events, handler); return; } void LeAdvertisingManager::StartAdvertising( AdvertiserId advertiser_id, const AdvertisingConfig config, uint16_t duration, base::OnceCallback status_callback, base::OnceCallback timeout_callback, common::Callback scan_callback, common::Callback set_terminated_callback, os::Handler* handler) { CallOn( pimpl_.get(), &impl::start_advertising, advertiser_id, config, duration, std::move(status_callback), std::move(timeout_callback), scan_callback, set_terminated_callback, handler); } void LeAdvertisingManager::RegisterAdvertiser( common::ContextualOnceCallback callback) { CallOn(pimpl_.get(), &impl::register_advertiser, std::move(callback)); } void LeAdvertisingManager::GetOwnAddress(uint8_t advertiser_id) { CallOn(pimpl_.get(), &impl::get_own_address, advertiser_id); } void LeAdvertisingManager::SetParameters(AdvertiserId advertiser_id, AdvertisingConfig config) { CallOn(pimpl_.get(), &impl::set_parameters, advertiser_id, config); } void LeAdvertisingManager::SetData(AdvertiserId advertiser_id, bool set_scan_rsp, std::vector 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 data) { CallOn(pimpl_.get(), &impl::set_periodic_data, advertiser_id, data); } void LeAdvertisingManager::EnablePeriodicAdvertising(AdvertiserId advertiser_id, bool enable, bool include_adi) { CallOn(pimpl_.get(), &impl::enable_periodic_advertising, advertiser_id, enable, include_adi); } 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