/* * Copyright 2022 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. */ #pragma once #include #include #include #include "common/callback.h" #include "common/init_flags.h" #include "hci/address_with_type.h" #include "hci/hci_packets.h" #include "hci/le_scanning_callback.h" #include "hci/le_scanning_interface.h" #include "hci/uuid.h" #include "module.h" #include "os/alarm.h" #include "os/log.h" namespace bluetooth { namespace hci { constexpr std::chrono::duration kPeriodicSyncTimeout = std::chrono::seconds(30); constexpr int kMaxSyncTransactions = 16; enum PeriodicSyncState : int { PERIODIC_SYNC_STATE_IDLE = 0, PERIODIC_SYNC_STATE_PENDING, PERIODIC_SYNC_STATE_ESTABLISHED, }; struct PeriodicSyncTransferStates { int pa_source; int connection_handle; Address addr; }; struct PeriodicSyncStates { int request_id; uint8_t advertiser_sid; AddressWithType address_with_type; uint16_t sync_handle; PeriodicSyncState sync_state; }; struct PendingPeriodicSyncRequest { PendingPeriodicSyncRequest( uint8_t advertiser_sid, AddressWithType address_with_type, uint16_t skip, uint16_t sync_timeout, os::Handler* handler) : advertiser_sid(advertiser_sid), address_with_type(std::move(address_with_type)), skip(skip), sync_timeout(sync_timeout), sync_timeout_alarm(handler) {} bool busy = false; uint8_t advertiser_sid; AddressWithType address_with_type; uint16_t skip; uint16_t sync_timeout; os::Alarm sync_timeout_alarm; }; class PeriodicSyncManager { public: explicit PeriodicSyncManager(ScanningCallback* callbacks) : le_scanning_interface_(nullptr), handler_(nullptr), callbacks_(callbacks), sync_received_callback_id(0) {} void Init(hci::LeScanningInterface* le_scanning_interface, os::Handler* handler) { le_scanning_interface_ = le_scanning_interface; handler_ = handler; } void SetScanningCallback(ScanningCallback* callbacks) { callbacks_ = callbacks; } void StartSync(const PeriodicSyncStates& request, uint16_t skip, uint16_t sync_timeout) { if (periodic_syncs_.size() >= kMaxSyncTransactions) { int status = static_cast(ErrorCode::CONNECTION_REJECTED_LIMITED_RESOURCES); callbacks_->OnPeriodicSyncStarted( request.request_id, status, 0, request.advertiser_sid, request.address_with_type, 0, 0); return; } auto address_type = request.address_with_type.GetAddressType(); ASSERT_LOG( (address_type == AddressType::PUBLIC_DEVICE_ADDRESS || address_type == AddressType::RANDOM_DEVICE_ADDRESS), "Invalid address type %s", AddressTypeText(address_type).c_str()); periodic_syncs_.emplace_back(request); LOG_DEBUG("address = %s, sid = %d", request.address_with_type.ToString().c_str(), request.advertiser_sid); pending_sync_requests_.emplace_back( request.advertiser_sid, request.address_with_type, skip, sync_timeout, handler_); HandleNextRequest(); } void StopSync(uint16_t handle) { LOG_DEBUG("[PSync]: handle = %u", handle); auto periodic_sync = GetEstablishedSyncFromHandle(handle); if (periodic_sync == periodic_syncs_.end()) { LOG_ERROR("[PSync]: invalid index for handle %u", handle); le_scanning_interface_->EnqueueCommand( hci::LePeriodicAdvertisingTerminateSyncBuilder::Create(handle), handler_->BindOnceOn( this, &PeriodicSyncManager::check_status)); return; }; periodic_syncs_.erase(periodic_sync); le_scanning_interface_->EnqueueCommand( hci::LePeriodicAdvertisingTerminateSyncBuilder::Create(handle), handler_->BindOnceOn(this, &PeriodicSyncManager::check_status)); } void CancelCreateSync(uint8_t adv_sid, Address address) { LOG_DEBUG("[PSync]"); auto periodic_sync = GetSyncFromAddressAndSid(address, adv_sid); if (periodic_sync == periodic_syncs_.end()) { LOG_ERROR("[PSync]:Invalid index for sid=%u", adv_sid); return; } if (periodic_sync->sync_state == PERIODIC_SYNC_STATE_PENDING) { LOG_WARN("[PSync]: Sync state is pending"); le_scanning_interface_->EnqueueCommand( hci::LePeriodicAdvertisingCreateSyncCancelBuilder::Create(), handler_->BindOnceOn(this, &PeriodicSyncManager::HandlePeriodicAdvertisingCreateSyncCancelStatus)); } else if (periodic_sync->sync_state == PERIODIC_SYNC_STATE_IDLE) { LOG_DEBUG("[PSync]: Removing Sync request from queue"); CleanUpRequest(adv_sid, address); } periodic_syncs_.erase(periodic_sync); } void TransferSync( const Address& address, uint16_t service_data, uint16_t sync_handle, int pa_source, uint16_t connection_handle) { if (periodic_sync_transfers_.size() >= kMaxSyncTransactions) { int status = static_cast(ErrorCode::CONNECTION_REJECTED_LIMITED_RESOURCES); callbacks_->OnPeriodicSyncTransferred(pa_source, status, address); return; } PeriodicSyncTransferStates request{pa_source, connection_handle, address}; periodic_sync_transfers_.emplace_back(request); le_scanning_interface_->EnqueueCommand( hci::LePeriodicAdvertisingSyncTransferBuilder::Create(connection_handle, service_data, sync_handle), handler_->BindOnceOn( this, &PeriodicSyncManager::HandlePeriodicAdvertisingSyncTransferComplete< LePeriodicAdvertisingSyncTransferCompleteView>, connection_handle)); } void SyncSetInfo( const Address& address, uint16_t service_data, uint8_t adv_handle, int pa_source, uint16_t connection_handle) { if (periodic_sync_transfers_.size() >= kMaxSyncTransactions) { int status = static_cast(ErrorCode::CONNECTION_REJECTED_LIMITED_RESOURCES); callbacks_->OnPeriodicSyncTransferred(pa_source, status, address); return; } PeriodicSyncTransferStates request{pa_source, connection_handle, address}; periodic_sync_transfers_.emplace_back(request); le_scanning_interface_->EnqueueCommand( hci::LePeriodicAdvertisingSetInfoTransferBuilder::Create(connection_handle, service_data, adv_handle), handler_->BindOnceOn( this, &PeriodicSyncManager::HandlePeriodicAdvertisingSyncTransferComplete< LePeriodicAdvertisingSetInfoTransferCompleteView>, connection_handle)); } void SyncTxParameters(const Address& address, uint8_t mode, uint16_t skip, uint16_t timeout, int reg_id) { LOG_DEBUG("[PAST]: mode=%u, skip=%u, timeout=%u", mode, skip, timeout); auto sync_cte_type = static_cast( static_cast(PeriodicSyncCteType::AVOID_AOA_CONSTANT_TONE_EXTENSION) | static_cast(PeriodicSyncCteType::AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_ONE_US_SLOTS) | static_cast(PeriodicSyncCteType::AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_TWO_US_SLOTS)); sync_received_callback_registered_ = true; sync_received_callback_id = reg_id; le_scanning_interface_->EnqueueCommand( hci::LeSetDefaultPeriodicAdvertisingSyncTransferParametersBuilder::Create( static_cast(mode), skip, timeout, sync_cte_type), handler_->BindOnceOn( this, &PeriodicSyncManager::check_status)); } void HandlePeriodicAdvertisingCreateSyncStatus(CommandStatusView) {} void HandlePeriodicAdvertisingCreateSyncCancelStatus(CommandCompleteView) {} template void HandlePeriodicAdvertisingSyncTransferComplete(uint16_t connection_handle, CommandCompleteView view) { ASSERT(view.IsValid()); auto status_view = View::Create(view); ASSERT(status_view.IsValid()); if (status_view.GetStatus() != ErrorCode::SUCCESS) { LOG_WARN( "Got a Command complete %s, status %s, connection_handle %d", OpCodeText(view.GetCommandOpCode()).c_str(), ErrorCodeText(status_view.GetStatus()).c_str(), connection_handle); } else { LOG_DEBUG( "Got a Command complete %s, status %s, connection_handle %d", OpCodeText(view.GetCommandOpCode()).c_str(), ErrorCodeText(status_view.GetStatus()).c_str(), connection_handle); } auto periodic_sync_transfer = GetSyncTransferRequestFromConnectionHandle(connection_handle); if (periodic_sync_transfer == periodic_sync_transfers_.end()) { LOG_ERROR("[PAST]:Invalid, conn_handle %u not found in DB", connection_handle); return; }; callbacks_->OnPeriodicSyncTransferred( periodic_sync_transfer->pa_source, (uint16_t)status_view.GetStatus(), periodic_sync_transfer->addr); periodic_sync_transfers_.erase(periodic_sync_transfer); } template 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_WARN( "Got a Command complete %s, status %s", OpCodeText(view.GetCommandOpCode()).c_str(), ErrorCodeText(status_view.GetStatus()).c_str()); } else { LOG_DEBUG( "Got a Command complete %s, status %s", OpCodeText(view.GetCommandOpCode()).c_str(), ErrorCodeText(status_view.GetStatus()).c_str()); } } void HandleLePeriodicAdvertisingSyncEstablished(LePeriodicAdvertisingSyncEstablishedView event_view) { ASSERT(event_view.IsValid()); LOG_DEBUG( "[PSync]: status=%d, sync_handle=%d, s_id=%d, " "address_type=%d, adv_phy=%d,adv_interval=%d, clock_acc=%d", (uint16_t)event_view.GetStatus(), event_view.GetSyncHandle(), event_view.GetAdvertisingSid(), (uint16_t)event_view.GetAdvertiserAddressType(), (uint16_t)event_view.GetAdvertiserPhy(), event_view.GetPeriodicAdvertisingInterval(), (uint16_t)event_view.GetAdvertiserClockAccuracy()); auto pending_sync_request = GetPendingSyncFromAddressAndSid(event_view.GetAdvertiserAddress(), event_view.GetAdvertisingSid()); if (pending_sync_request != pending_sync_requests_.end()) { pending_sync_request->sync_timeout_alarm.Cancel(); } auto address_with_type = AddressWithType(event_view.GetAdvertiserAddress(), event_view.GetAdvertiserAddressType()); auto peer_address_type = address_with_type.GetAddressType(); AddressType temp_address_type; switch (peer_address_type) { case AddressType::PUBLIC_DEVICE_ADDRESS: case AddressType::PUBLIC_IDENTITY_ADDRESS: temp_address_type = AddressType::PUBLIC_DEVICE_ADDRESS; break; case AddressType::RANDOM_DEVICE_ADDRESS: case AddressType::RANDOM_IDENTITY_ADDRESS: temp_address_type = AddressType::RANDOM_DEVICE_ADDRESS; break; } auto periodic_sync = GetSyncFromAddressWithTypeAndSid( AddressWithType(event_view.GetAdvertiserAddress(), temp_address_type), event_view.GetAdvertisingSid()); if (periodic_sync == periodic_syncs_.end()) { LOG_WARN("[PSync]: Invalid address and sid for sync established"); if (event_view.GetStatus() == ErrorCode::SUCCESS) { LOG_WARN("Terminate sync"); le_scanning_interface_->EnqueueCommand( hci::LePeriodicAdvertisingTerminateSyncBuilder::Create(event_view.GetSyncHandle()), handler_->BindOnceOn( this, &PeriodicSyncManager::check_status)); } AdvanceRequest(); return; } periodic_sync->sync_handle = event_view.GetSyncHandle(); periodic_sync->sync_state = PERIODIC_SYNC_STATE_ESTABLISHED; callbacks_->OnPeriodicSyncStarted( periodic_sync->request_id, (uint8_t)event_view.GetStatus(), event_view.GetSyncHandle(), event_view.GetAdvertisingSid(), address_with_type, (uint16_t)event_view.GetAdvertiserPhy(), event_view.GetPeriodicAdvertisingInterval()); AdvanceRequest(); } void HandleLePeriodicAdvertisingReport(LePeriodicAdvertisingReportView event_view) { ASSERT(event_view.IsValid()); LOG_DEBUG( "[PSync]: sync_handle = %u, tx_power = %d, rssi = %d," "cte_type = %u, data_status = %u, data_len = %u", event_view.GetSyncHandle(), event_view.GetTxPower(), event_view.GetRssi(), (uint16_t)event_view.GetCteType(), (uint16_t)event_view.GetDataStatus(), (uint16_t)event_view.GetData().size()); uint16_t sync_handle = event_view.GetSyncHandle(); auto periodic_sync = GetEstablishedSyncFromHandle(sync_handle); if (periodic_sync == periodic_syncs_.end()) { LOG_ERROR("[PSync]: index not found for handle %u", sync_handle); return; } LOG_DEBUG("%s", "[PSync]: invoking callback"); callbacks_->OnPeriodicSyncReport( sync_handle, event_view.GetTxPower(), event_view.GetRssi(), (uint16_t)event_view.GetDataStatus(), event_view.GetData()); } void HandleLePeriodicAdvertisingSyncLost(LePeriodicAdvertisingSyncLostView event_view) { ASSERT(event_view.IsValid()); uint16_t sync_handle = event_view.GetSyncHandle(); LOG_DEBUG("[PSync]: sync_handle = %d", sync_handle); callbacks_->OnPeriodicSyncLost(sync_handle); auto periodic_sync = GetEstablishedSyncFromHandle(sync_handle); periodic_syncs_.erase(periodic_sync); } void HandleLePeriodicAdvertisingSyncTransferReceived(LePeriodicAdvertisingSyncTransferReceivedView event_view) { ASSERT(event_view.IsValid()); uint8_t status = (uint8_t)event_view.GetStatus(); uint8_t advertiser_phy = (uint8_t)event_view.GetAdvertiserPhy(); LOG_DEBUG( "[PAST]: status = %u, connection_handle = %u, service_data = %u," " sync_handle = %u, adv_sid = %u, address_type = %u, address = %s," " advertiser_phy = %u, periodic_advertising_interval = %u, clock_accuracy = %u", status, event_view.GetConnectionHandle(), event_view.GetServiceData(), event_view.GetSyncHandle(), event_view.GetAdvertisingSid(), (uint8_t)event_view.GetAdvertiserAddressType(), event_view.GetAdvertiserAddress().ToString().c_str(), advertiser_phy, event_view.GetPeriodicAdvertisingInterval(), (uint8_t)event_view.GetAdvertiserClockAccuracy()); if (sync_received_callback_registered_) { callbacks_->OnPeriodicSyncStarted( sync_received_callback_id, status, event_view.GetSyncHandle(), event_view.GetAdvertisingSid(), AddressWithType(event_view.GetAdvertiserAddress(), event_view.GetAdvertiserAddressType()), advertiser_phy, event_view.GetPeriodicAdvertisingInterval()); } } void OnStartSyncTimeout() { auto& request = pending_sync_requests_.front(); LOG_WARN( "%s: sync timeout SID=%04X, bd_addr=%s", __func__, request.advertiser_sid, request.address_with_type.ToString().c_str()); uint8_t adv_sid = request.advertiser_sid; AddressWithType address_with_type = request.address_with_type; auto sync = GetSyncFromAddressWithTypeAndSid(address_with_type, adv_sid); le_scanning_interface_->EnqueueCommand( hci::LePeriodicAdvertisingCreateSyncCancelBuilder::Create(), handler_->BindOnceOn(this, &PeriodicSyncManager::HandlePeriodicAdvertisingCreateSyncCancelStatus)); int status = static_cast(ErrorCode::ADVERTISING_TIMEOUT); callbacks_->OnPeriodicSyncStarted( sync->request_id, status, 0, sync->advertiser_sid, request.address_with_type, 0, 0); RemoveSyncRequest(sync); } private: std::list::iterator GetEstablishedSyncFromHandle(uint16_t handle) { for (auto it = periodic_syncs_.begin(); it != periodic_syncs_.end(); it++) { if (it->sync_handle == handle && it->sync_state == PeriodicSyncState::PERIODIC_SYNC_STATE_ESTABLISHED) { return it; } } return periodic_syncs_.end(); } std::list::iterator GetSyncFromAddressWithTypeAndSid( const AddressWithType& address_with_type, uint8_t adv_sid) { for (auto it = periodic_syncs_.begin(); it != periodic_syncs_.end(); it++) { if (it->advertiser_sid == adv_sid && it->address_with_type == address_with_type) { return it; } } return periodic_syncs_.end(); } std::list::iterator GetSyncFromAddressAndSid(const Address& address, uint8_t adv_sid) { for (auto it = periodic_syncs_.begin(); it != periodic_syncs_.end(); it++) { if (it->advertiser_sid == adv_sid && it->address_with_type.GetAddress() == address) { return it; } } return periodic_syncs_.end(); } std::list::iterator GetPendingSyncFromAddressAndSid( const Address& address, uint8_t adv_sid) { for (auto it = pending_sync_requests_.begin(); it != pending_sync_requests_.end(); it++) { if (it->advertiser_sid == adv_sid && it->address_with_type.GetAddress() == address) { return it; } } return pending_sync_requests_.end(); } void RemoveSyncRequest(std::list::iterator it) { periodic_syncs_.erase(it); } std::list::iterator GetSyncTransferRequestFromConnectionHandle( uint16_t connection_handle) { for (auto it = periodic_sync_transfers_.begin(); it != periodic_sync_transfers_.end(); it++) { if (it->connection_handle == connection_handle) { return it; } } return periodic_sync_transfers_.end(); } void HandleStartSyncRequest(uint8_t sid, const AddressWithType& address_with_type, uint16_t skip, uint16_t timeout) { auto options = static_cast(0); auto sync_cte_type = static_cast( static_cast(PeriodicSyncCteType::AVOID_AOA_CONSTANT_TONE_EXTENSION) | static_cast(PeriodicSyncCteType::AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_ONE_US_SLOTS) | static_cast(PeriodicSyncCteType::AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_TWO_US_SLOTS)); auto sync = GetSyncFromAddressWithTypeAndSid(address_with_type, sid); sync->sync_state = PERIODIC_SYNC_STATE_PENDING; AdvertisingAddressType advertisingAddressType = static_cast(address_with_type.GetAddressType()); le_scanning_interface_->EnqueueCommand( hci::LePeriodicAdvertisingCreateSyncBuilder::Create( options, sid, advertisingAddressType, address_with_type.GetAddress(), skip, timeout, sync_cte_type), handler_->BindOnceOn(this, &PeriodicSyncManager::HandlePeriodicAdvertisingCreateSyncStatus)); } void HandleNextRequest() { if (pending_sync_requests_.empty()) { LOG_DEBUG("pending_sync_requests_ empty"); return; } auto& request = pending_sync_requests_.front(); LOG_INFO( "executing sync request SID=%04X, bd_addr=%s", request.advertiser_sid, request.address_with_type.ToString().c_str()); if (request.busy) { LOG_INFO("Request is already busy"); return; } request.busy = true; request.sync_timeout_alarm.Cancel(); HandleStartSyncRequest(request.advertiser_sid, request.address_with_type, request.skip, request.sync_timeout); request.sync_timeout_alarm.Schedule( base::BindOnce(&PeriodicSyncManager::OnStartSyncTimeout, base::Unretained(this)), kPeriodicSyncTimeout); } void AdvanceRequest() { LOG_DEBUG("AdvanceRequest"); if (pending_sync_requests_.empty()) { LOG_DEBUG("pending_sync_requests_ empty"); return; } auto it = pending_sync_requests_.begin(); pending_sync_requests_.erase(it); HandleNextRequest(); } void CleanUpRequest(uint8_t advertiser_sid, Address address) { auto it = pending_sync_requests_.begin(); while (it != pending_sync_requests_.end()) { if (it->advertiser_sid == advertiser_sid && it->address_with_type.GetAddress() == address) { LOG_INFO( "removing connection request SID=%04X, bd_addr=%s, busy=%d", it->advertiser_sid, it->address_with_type.GetAddress().ToString().c_str(), it->busy); it = pending_sync_requests_.erase(it); } else { ++it; } } } hci::LeScanningInterface* le_scanning_interface_; os::Handler* handler_; ScanningCallback* callbacks_; std::list pending_sync_requests_; std::list periodic_syncs_; std::list periodic_sync_transfers_; bool sync_received_callback_registered_ = false; int sync_received_callback_id{}; }; } // namespace hci } // namespace bluetooth