blob: a1f52be00b2036dfc37631112e8b505441975c3a [file] [log] [blame]
/*
* 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 <chrono>
#include <memory>
#include <utility>
#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<int>(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<LePeriodicAdvertisingTerminateSyncCompleteView>));
return;
};
periodic_syncs_.erase(periodic_sync);
le_scanning_interface_->EnqueueCommand(
hci::LePeriodicAdvertisingTerminateSyncBuilder::Create(handle),
handler_->BindOnceOn(this, &PeriodicSyncManager::check_status<LePeriodicAdvertisingTerminateSyncCompleteView>));
}
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<int>(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<int>(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<CteType>(
static_cast<uint8_t>(PeriodicSyncCteType::AVOID_AOA_CONSTANT_TONE_EXTENSION) |
static_cast<uint8_t>(PeriodicSyncCteType::AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_ONE_US_SLOTS) |
static_cast<uint8_t>(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<SyncTransferMode>(mode), skip, timeout, sync_cte_type),
handler_->BindOnceOn(
this,
&PeriodicSyncManager::check_status<LeSetDefaultPeriodicAdvertisingSyncTransferParametersCompleteView>));
}
void HandlePeriodicAdvertisingCreateSyncStatus(CommandStatusView) {}
void HandlePeriodicAdvertisingCreateSyncCancelStatus(CommandCompleteView) {}
template <class View>
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 <class View>
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 temp_address_type = address_with_type.GetAddressType();
// If the create sync command uses 0x01, Random or Random ID, the result can be 0x01, 0x02, or 0x03,
// because a Random Address, if it is an RPA, can be resolved to either Public Identity or Random Identity.
if (temp_address_type != AddressType::PUBLIC_DEVICE_ADDRESS) {
temp_address_type = AddressType::RANDOM_DEVICE_ADDRESS;
}
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<LePeriodicAdvertisingTerminateSyncCompleteView>));
}
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<int>(ErrorCode::ADVERTISING_TIMEOUT);
callbacks_->OnPeriodicSyncStarted(
sync->request_id, status, 0, sync->advertiser_sid, request.address_with_type, 0, 0);
RemoveSyncRequest(sync);
}
private:
std::list<PeriodicSyncStates>::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<PeriodicSyncStates>::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<PeriodicSyncStates>::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<PendingPeriodicSyncRequest>::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<PeriodicSyncStates>::iterator it) {
periodic_syncs_.erase(it);
}
std::list<PeriodicSyncTransferStates>::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<PeriodicAdvertisingOptions>(0);
auto sync_cte_type = static_cast<PeriodicSyncCteType>(
static_cast<uint8_t>(PeriodicSyncCteType::AVOID_AOA_CONSTANT_TONE_EXTENSION) |
static_cast<uint8_t>(PeriodicSyncCteType::AVOID_AOD_CONSTANT_TONE_EXTENSION_WITH_ONE_US_SLOTS) |
static_cast<uint8_t>(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<AdvertisingAddressType>(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<PendingPeriodicSyncRequest> pending_sync_requests_;
std::list<PeriodicSyncStates> periodic_syncs_;
std::list<PeriodicSyncTransferStates> periodic_sync_transfers_;
bool sync_received_callback_registered_ = false;
int sync_received_callback_id{};
};
} // namespace hci
} // namespace bluetooth