You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
560 lines
24 KiB
560 lines
24 KiB
/*
|
|
* Copyright 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "l2cap/le/internal/signalling_manager.h"
|
|
|
|
#include <chrono>
|
|
|
|
#include "common/bind.h"
|
|
#include "l2cap/internal/data_pipeline_manager.h"
|
|
#include "l2cap/internal/dynamic_channel_impl.h"
|
|
#include "l2cap/internal/le_credit_based_channel_data_controller.h"
|
|
#include "l2cap/l2cap_packets.h"
|
|
#include "l2cap/le/internal/link.h"
|
|
#include "os/log.h"
|
|
#include "packet/raw_builder.h"
|
|
|
|
namespace bluetooth {
|
|
namespace l2cap {
|
|
namespace le {
|
|
namespace internal {
|
|
|
|
static constexpr auto kTimeout = std::chrono::seconds(3);
|
|
|
|
LeSignallingManager::LeSignallingManager(os::Handler* handler, Link* link,
|
|
l2cap::internal::DataPipelineManager* data_pipeline_manager,
|
|
DynamicChannelServiceManagerImpl* dynamic_service_manager,
|
|
l2cap::internal::DynamicChannelAllocator* channel_allocator)
|
|
: handler_(handler), link_(link), data_pipeline_manager_(data_pipeline_manager),
|
|
dynamic_service_manager_(dynamic_service_manager), channel_allocator_(channel_allocator), alarm_(handler) {
|
|
ASSERT(handler_ != nullptr);
|
|
ASSERT(link_ != nullptr);
|
|
signalling_channel_ =
|
|
link_->AllocateFixedChannel(kLeSignallingCid, SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK);
|
|
signalling_channel_->GetQueueUpEnd()->RegisterDequeue(
|
|
handler_, common::Bind(&LeSignallingManager::on_incoming_packet, common::Unretained(this)));
|
|
enqueue_buffer_ =
|
|
std::make_unique<os::EnqueueBuffer<packet::BasePacketBuilder>>(signalling_channel_->GetQueueUpEnd());
|
|
}
|
|
|
|
LeSignallingManager::~LeSignallingManager() {
|
|
enqueue_buffer_.reset();
|
|
signalling_channel_->GetQueueUpEnd()->UnregisterDequeue();
|
|
signalling_channel_ = nullptr;
|
|
}
|
|
|
|
void LeSignallingManager::SendConnectionRequest(Psm psm, Cid local_cid, Mtu mtu) {
|
|
dynamic_service_manager_->GetSecurityEnforcementInterface()->Enforce(
|
|
link_->GetDevice(),
|
|
dynamic_service_manager_->GetService(psm)->GetSecurityPolicy(),
|
|
handler_->BindOnceOn(this, &LeSignallingManager::on_security_result_for_outgoing, psm, local_cid, mtu));
|
|
}
|
|
|
|
void LeSignallingManager::on_security_result_for_outgoing(Psm psm, Cid local_cid, Mtu mtu, bool result) {
|
|
if (!result) {
|
|
LOG_WARN("Security requirement can't be satisfied. Dropping connection request");
|
|
return;
|
|
}
|
|
|
|
PendingCommand pending_command = PendingCommand::CreditBasedConnectionRequest(
|
|
next_signal_id_, psm, local_cid, mtu, link_->GetMps(), link_->GetInitialCredit());
|
|
next_signal_id_++;
|
|
pending_commands_.push(pending_command);
|
|
if (pending_commands_.size() == 1) {
|
|
handle_send_next_command();
|
|
}
|
|
}
|
|
|
|
void LeSignallingManager::SendDisconnectRequest(Cid scid, Cid dcid) {
|
|
PendingCommand pending_command = PendingCommand::DisconnectionRequest(next_signal_id_, scid, dcid);
|
|
next_signal_id_++;
|
|
pending_commands_.push(pending_command);
|
|
if (pending_commands_.size() == 1) {
|
|
handle_send_next_command();
|
|
}
|
|
}
|
|
|
|
void LeSignallingManager::SendConnectionParameterUpdateRequest(
|
|
uint16_t interval_min, uint16_t interval_max, uint16_t peripheral_latency, uint16_t timeout_multiplier) {
|
|
PendingCommand pending_command = PendingCommand::ConnectionParameterUpdate(
|
|
next_signal_id_, interval_min, interval_max, peripheral_latency, timeout_multiplier);
|
|
next_signal_id_++;
|
|
pending_commands_.push(pending_command);
|
|
if (pending_commands_.size() == 1) {
|
|
handle_send_next_command();
|
|
}
|
|
}
|
|
|
|
void LeSignallingManager::SendConnectionParameterUpdateResponse(SignalId signal_id,
|
|
ConnectionParameterUpdateResponseResult result) {
|
|
auto builder = ConnectionParameterUpdateResponseBuilder::Create(signal_id.Value(), result);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
}
|
|
|
|
void LeSignallingManager::SendCredit(Cid local_cid, uint16_t credits) {
|
|
auto builder = LeFlowControlCreditBuilder::Create(next_signal_id_.Value(), local_cid, credits);
|
|
next_signal_id_++;
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
}
|
|
|
|
void LeSignallingManager::SendEnhancedConnectionRequest(Psm psm, std::vector<Cid> local_cid, Mtu mtu) {}
|
|
|
|
void LeSignallingManager::SendEnhancedReconfigureRequest(std::vector<Cid> local_cid, Mtu mtu) {}
|
|
|
|
void LeSignallingManager::CancelAlarm() {
|
|
alarm_.Cancel();
|
|
}
|
|
|
|
void LeSignallingManager::OnCommandReject(LeCommandRejectView command_reject_view) {
|
|
auto signal_id = command_reject_view.GetIdentifier();
|
|
if (signal_id != command_just_sent_.signal_id_) {
|
|
LOG_WARN("Unexpected response: no pending request");
|
|
return;
|
|
}
|
|
alarm_.Cancel();
|
|
if (command_just_sent_.command_code_ == LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST) {
|
|
link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_,
|
|
LeCreditBasedConnectionResponseResult::NO_RESOURCES_AVAILABLE);
|
|
}
|
|
handle_send_next_command();
|
|
|
|
LOG_WARN("Command rejected");
|
|
}
|
|
|
|
void LeSignallingManager::OnConnectionParameterUpdateRequest(
|
|
SignalId signal_id,
|
|
uint16_t interval_min,
|
|
uint16_t interval_max,
|
|
uint16_t peripheral_latency,
|
|
uint16_t timeout_multiplier) {
|
|
if (link_->GetRole() == hci::Role::PERIPHERAL) {
|
|
LOG_WARN("Received request from LL central");
|
|
auto builder = LeCommandRejectNotUnderstoodBuilder::Create(signal_id.Value());
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
return;
|
|
}
|
|
|
|
if (!link_->CheckConnectionParameters(interval_min, interval_max, peripheral_latency, timeout_multiplier)) {
|
|
LOG_WARN("Received invalid connection parameter update request from LL central");
|
|
auto builder = ConnectionParameterUpdateResponseBuilder::Create(signal_id.Value(),
|
|
ConnectionParameterUpdateResponseResult::REJECTED);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
return;
|
|
}
|
|
link_->UpdateConnectionParameterFromRemote(
|
|
signal_id, interval_min, interval_max, peripheral_latency, timeout_multiplier);
|
|
}
|
|
|
|
void LeSignallingManager::OnConnectionParameterUpdateResponse(SignalId signal_id,
|
|
ConnectionParameterUpdateResponseResult result) {
|
|
if (signal_id != command_just_sent_.signal_id_) {
|
|
LOG_WARN("Unexpected response: no pending request");
|
|
return;
|
|
}
|
|
if (command_just_sent_.command_code_ != LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST) {
|
|
LOG_WARN("Unexpected response: no pending request");
|
|
return;
|
|
}
|
|
alarm_.Cancel();
|
|
command_just_sent_.signal_id_ = kInitialSignalId;
|
|
if (result != ConnectionParameterUpdateResponseResult::ACCEPTED) {
|
|
LOG_ERROR("Connection parameter update is not accepted");
|
|
}
|
|
}
|
|
|
|
void LeSignallingManager::OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid, Mtu mtu, uint16_t mps,
|
|
uint16_t initial_credits) {
|
|
if (remote_cid == kInvalidCid) {
|
|
LOG_WARN("Invalid remote cid received from remote psm:%d remote_cid:%d", psm, remote_cid);
|
|
send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
|
|
LeCreditBasedConnectionResponseResult::INVALID_SOURCE_CID);
|
|
return;
|
|
}
|
|
|
|
if (channel_allocator_->IsPsmUsed(psm)) {
|
|
LOG_WARN("Psm already exists");
|
|
send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
|
|
LeCreditBasedConnectionResponseResult::LE_PSM_NOT_SUPPORTED);
|
|
return;
|
|
}
|
|
|
|
if (!dynamic_service_manager_->IsServiceRegistered(psm)) {
|
|
LOG_INFO("Service for this psm (%d) is not registered", psm);
|
|
send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
|
|
LeCreditBasedConnectionResponseResult::LE_PSM_NOT_SUPPORTED);
|
|
return;
|
|
}
|
|
|
|
PendingConnection pending{
|
|
.remote_cid = remote_cid,
|
|
.incoming_signal_id = signal_id,
|
|
.initial_credits = initial_credits,
|
|
.max_pdu_size = mps,
|
|
.mtu = mtu,
|
|
};
|
|
dynamic_service_manager_->GetSecurityEnforcementInterface()->Enforce(
|
|
link_->GetDevice(),
|
|
dynamic_service_manager_->GetService(psm)->GetSecurityPolicy(),
|
|
handler_->BindOnceOn(this, &LeSignallingManager::on_security_result_for_incoming, psm, pending));
|
|
}
|
|
|
|
void LeSignallingManager::on_security_result_for_incoming(Psm psm, PendingConnection request, bool result) {
|
|
auto signal_id = request.incoming_signal_id;
|
|
auto* service = dynamic_service_manager_->GetService(psm);
|
|
if (!result) {
|
|
auto security_policy = service->GetSecurityPolicy();
|
|
switch (security_policy) {
|
|
case SecurityPolicy::NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK:
|
|
LOG_ERROR("If no security requirement, we should never fail");
|
|
break;
|
|
case SecurityPolicy::ENCRYPTED_TRANSPORT:
|
|
send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
|
|
LeCreditBasedConnectionResponseResult::INSUFFICIENT_AUTHENTICATION);
|
|
return;
|
|
case SecurityPolicy::AUTHENTICATED_ENCRYPTED_TRANSPORT:
|
|
case SecurityPolicy::BEST:
|
|
send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
|
|
LeCreditBasedConnectionResponseResult::INSUFFICIENT_AUTHENTICATION);
|
|
return;
|
|
case SecurityPolicy::_NOT_FOR_YOU__AUTHENTICATED_PAIRING_WITH_128_BIT_KEY:
|
|
send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
|
|
LeCreditBasedConnectionResponseResult::INSUFFICIENT_ENCRYPTION_KEY_SIZE);
|
|
return;
|
|
case SecurityPolicy::_NOT_FOR_YOU__AUTHORIZATION:
|
|
send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
|
|
LeCreditBasedConnectionResponseResult::INSUFFICIENT_AUTHORIZATION);
|
|
return;
|
|
}
|
|
}
|
|
auto config = service->GetConfigOption();
|
|
auto local_mtu = config.mtu;
|
|
auto local_mps = link_->GetMps();
|
|
|
|
auto new_channel = link_->AllocateDynamicChannel(psm, request.remote_cid);
|
|
if (new_channel == nullptr) {
|
|
LOG_WARN("Can't allocate dynamic channel");
|
|
// TODO: We need to respond with the correct reason
|
|
send_connection_response(signal_id, kInvalidCid, 0, 0, 0,
|
|
LeCreditBasedConnectionResponseResult::SOURCE_CID_ALREADY_ALLOCATED);
|
|
return;
|
|
}
|
|
|
|
send_connection_response(signal_id, new_channel->GetCid(), local_mtu, local_mps, link_->GetInitialCredit(),
|
|
LeCreditBasedConnectionResponseResult::SUCCESS);
|
|
auto* data_controller = reinterpret_cast<l2cap::internal::LeCreditBasedDataController*>(
|
|
data_pipeline_manager_->GetDataController(new_channel->GetCid()));
|
|
auto actual_mtu = std::min(request.mtu, local_mtu);
|
|
data_controller->SetMtu(actual_mtu);
|
|
data_controller->SetMps(std::min(request.max_pdu_size, local_mps));
|
|
data_controller->OnCredit(request.initial_credits);
|
|
auto user_channel = std::make_unique<DynamicChannel>(new_channel, handler_, link_, actual_mtu);
|
|
dynamic_service_manager_->GetService(psm)->NotifyChannelCreation(std::move(user_channel));
|
|
}
|
|
|
|
void LeSignallingManager::OnConnectionResponse(SignalId signal_id, Cid remote_cid, Mtu mtu, uint16_t mps,
|
|
uint16_t initial_credits, LeCreditBasedConnectionResponseResult result) {
|
|
if (signal_id != command_just_sent_.signal_id_) {
|
|
LOG_WARN("Unexpected response: no pending request");
|
|
return;
|
|
}
|
|
if (command_just_sent_.command_code_ != LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST) {
|
|
LOG_WARN("Unexpected response: no pending request");
|
|
return;
|
|
}
|
|
alarm_.Cancel();
|
|
command_just_sent_.signal_id_ = kInitialSignalId;
|
|
if (result != LeCreditBasedConnectionResponseResult::SUCCESS) {
|
|
LOG_WARN("Connection failed: %s", LeCreditBasedConnectionResponseResultText(result).data());
|
|
link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_, result);
|
|
handle_send_next_command();
|
|
return;
|
|
}
|
|
auto new_channel =
|
|
link_->AllocateReservedDynamicChannel(command_just_sent_.source_cid_, command_just_sent_.psm_, remote_cid);
|
|
if (new_channel == nullptr) {
|
|
LOG_WARN("Can't allocate dynamic channel");
|
|
link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_,
|
|
LeCreditBasedConnectionResponseResult::NO_RESOURCES_AVAILABLE);
|
|
handle_send_next_command();
|
|
return;
|
|
}
|
|
auto* data_controller = reinterpret_cast<l2cap::internal::LeCreditBasedDataController*>(
|
|
data_pipeline_manager_->GetDataController(new_channel->GetCid()));
|
|
auto actual_mtu = std::min(mtu, command_just_sent_.mtu_);
|
|
data_controller->SetMtu(actual_mtu);
|
|
data_controller->SetMps(std::min(mps, command_just_sent_.mps_));
|
|
data_controller->OnCredit(initial_credits);
|
|
std::unique_ptr<DynamicChannel> user_channel =
|
|
std::make_unique<DynamicChannel>(new_channel, handler_, link_, actual_mtu);
|
|
link_->NotifyChannelCreation(new_channel->GetCid(), std::move(user_channel));
|
|
}
|
|
|
|
void LeSignallingManager::OnDisconnectionRequest(SignalId signal_id, Cid cid, Cid remote_cid) {
|
|
auto channel = channel_allocator_->FindChannelByCid(cid);
|
|
if (channel == nullptr) {
|
|
LOG_WARN("Disconnect request for an unknown channel");
|
|
return;
|
|
}
|
|
if (channel->GetRemoteCid() != remote_cid) {
|
|
LOG_WARN("Disconnect request for an unmatching channel");
|
|
return;
|
|
}
|
|
auto builder = LeDisconnectionResponseBuilder::Create(signal_id.Value(), cid, remote_cid);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
channel->OnClosed(hci::ErrorCode::SUCCESS);
|
|
link_->FreeDynamicChannel(cid);
|
|
}
|
|
|
|
void LeSignallingManager::OnDisconnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid) {
|
|
if (signal_id != command_just_sent_.signal_id_ ||
|
|
command_just_sent_.command_code_ != LeCommandCode::DISCONNECTION_REQUEST) {
|
|
LOG_WARN("Unexpected response: no pending request");
|
|
return;
|
|
}
|
|
if (command_just_sent_.source_cid_ != cid || command_just_sent_.destination_cid_ != remote_cid) {
|
|
LOG_WARN("Unexpected response: cid doesn't match. Expected scid %d dcid %d, got scid %d dcid %d",
|
|
command_just_sent_.source_cid_, command_just_sent_.destination_cid_, cid, remote_cid);
|
|
handle_send_next_command();
|
|
return;
|
|
}
|
|
alarm_.Cancel();
|
|
command_just_sent_.signal_id_ = kInitialSignalId;
|
|
auto channel = channel_allocator_->FindChannelByCid(cid);
|
|
if (channel == nullptr) {
|
|
LOG_WARN("Disconnect response for an unknown channel");
|
|
handle_send_next_command();
|
|
return;
|
|
}
|
|
|
|
channel->OnClosed(hci::ErrorCode::SUCCESS);
|
|
link_->FreeDynamicChannel(cid);
|
|
handle_send_next_command();
|
|
}
|
|
|
|
void LeSignallingManager::OnCredit(Cid remote_cid, uint16_t credits) {
|
|
auto channel = channel_allocator_->FindChannelByRemoteCid(remote_cid);
|
|
if (channel == nullptr) {
|
|
LOG_WARN("Received credit for invalid cid %d", channel->GetCid());
|
|
return;
|
|
}
|
|
auto* data_controller = reinterpret_cast<l2cap::internal::LeCreditBasedDataController*>(
|
|
data_pipeline_manager_->GetDataController(channel->GetCid()));
|
|
data_controller->OnCredit(credits);
|
|
}
|
|
|
|
void LeSignallingManager::on_incoming_packet() {
|
|
auto packet = signalling_channel_->GetQueueUpEnd()->TryDequeue();
|
|
LeControlView control_packet_view = LeControlView::Create(*packet);
|
|
if (!control_packet_view.IsValid()) {
|
|
LOG_WARN("Invalid signalling packet received");
|
|
return;
|
|
}
|
|
auto code = control_packet_view.GetCode();
|
|
switch (code) {
|
|
case LeCommandCode::COMMAND_REJECT: {
|
|
LeCommandRejectView command_reject_view = LeCommandRejectView::Create(control_packet_view);
|
|
if (!command_reject_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnCommandReject(command_reject_view);
|
|
return;
|
|
}
|
|
|
|
case LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST: {
|
|
ConnectionParameterUpdateRequestView parameter_update_req_view =
|
|
ConnectionParameterUpdateRequestView::Create(control_packet_view);
|
|
if (!parameter_update_req_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnConnectionParameterUpdateRequest(
|
|
parameter_update_req_view.GetIdentifier(),
|
|
parameter_update_req_view.GetIntervalMin(),
|
|
parameter_update_req_view.GetIntervalMax(),
|
|
parameter_update_req_view.GetPeripheralLatency(),
|
|
parameter_update_req_view.GetTimeoutMultiplier());
|
|
return;
|
|
}
|
|
case LeCommandCode::CONNECTION_PARAMETER_UPDATE_RESPONSE: {
|
|
ConnectionParameterUpdateResponseView parameter_update_rsp_view =
|
|
ConnectionParameterUpdateResponseView::Create(control_packet_view);
|
|
if (!parameter_update_rsp_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnConnectionParameterUpdateResponse(parameter_update_rsp_view.GetIdentifier(),
|
|
parameter_update_rsp_view.GetResult());
|
|
return;
|
|
}
|
|
case LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST: {
|
|
LeCreditBasedConnectionRequestView connection_request_view =
|
|
LeCreditBasedConnectionRequestView::Create(control_packet_view);
|
|
if (!connection_request_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnConnectionRequest(connection_request_view.GetIdentifier(), connection_request_view.GetLePsm(),
|
|
connection_request_view.GetSourceCid(), connection_request_view.GetMtu(),
|
|
connection_request_view.GetMps(), connection_request_view.GetInitialCredits());
|
|
return;
|
|
}
|
|
case LeCommandCode::LE_CREDIT_BASED_CONNECTION_RESPONSE: {
|
|
LeCreditBasedConnectionResponseView connection_response_view =
|
|
LeCreditBasedConnectionResponseView::Create(control_packet_view);
|
|
if (!connection_response_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnConnectionResponse(connection_response_view.GetIdentifier(), connection_response_view.GetDestinationCid(),
|
|
connection_response_view.GetMtu(), connection_response_view.GetMps(),
|
|
connection_response_view.GetInitialCredits(), connection_response_view.GetResult());
|
|
return;
|
|
}
|
|
case LeCommandCode::LE_FLOW_CONTROL_CREDIT: {
|
|
LeFlowControlCreditView credit_view = LeFlowControlCreditView::Create(control_packet_view);
|
|
if (!credit_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnCredit(credit_view.GetCid(), credit_view.GetCredits());
|
|
return;
|
|
}
|
|
case LeCommandCode::DISCONNECTION_REQUEST: {
|
|
LeDisconnectionRequestView disconnection_request_view = LeDisconnectionRequestView::Create(control_packet_view);
|
|
if (!disconnection_request_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnDisconnectionRequest(disconnection_request_view.GetIdentifier(), disconnection_request_view.GetDestinationCid(),
|
|
disconnection_request_view.GetSourceCid());
|
|
return;
|
|
}
|
|
case LeCommandCode::DISCONNECTION_RESPONSE: {
|
|
LeDisconnectionResponseView disconnection_response_view =
|
|
LeDisconnectionResponseView::Create(control_packet_view);
|
|
if (!disconnection_response_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnDisconnectionResponse(disconnection_response_view.GetIdentifier(),
|
|
disconnection_response_view.GetDestinationCid(),
|
|
disconnection_response_view.GetSourceCid());
|
|
return;
|
|
}
|
|
case LeCommandCode::CREDIT_BASED_CONNECTION_REQUEST: {
|
|
LeEnhancedCreditBasedConnectionRequestView request_view =
|
|
LeEnhancedCreditBasedConnectionRequestView::Create(control_packet_view);
|
|
if (!request_view.IsValid()) {
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
case LeCommandCode::CREDIT_BASED_CONNECTION_RESPONSE: {
|
|
LeEnhancedCreditBasedConnectionResponseView response_view =
|
|
LeEnhancedCreditBasedConnectionResponseView::Create(control_packet_view);
|
|
if (!response_view.IsValid()) {
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
case LeCommandCode::CREDIT_BASED_RECONFIGURE_REQUEST: {
|
|
LeEnhancedCreditBasedReconfigureRequestView request_view =
|
|
LeEnhancedCreditBasedReconfigureRequestView::Create(control_packet_view);
|
|
if (!request_view.IsValid()) {
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
case LeCommandCode::CREDIT_BASED_RECONFIGURE_RESPONSE: {
|
|
LeEnhancedCreditBasedReconfigureResponseView response_view =
|
|
LeEnhancedCreditBasedReconfigureResponseView::Create(control_packet_view);
|
|
if (!response_view.IsValid()) {
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
default:
|
|
LOG_WARN("Unhandled event 0x%x", static_cast<int>(code));
|
|
auto builder = LeCommandRejectNotUnderstoodBuilder::Create(control_packet_view.GetIdentifier());
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void LeSignallingManager::send_connection_response(SignalId signal_id, Cid local_cid, Mtu mtu, uint16_t mps,
|
|
uint16_t initial_credit,
|
|
LeCreditBasedConnectionResponseResult result) {
|
|
auto builder =
|
|
LeCreditBasedConnectionResponseBuilder::Create(signal_id.Value(), local_cid, mtu, mps, initial_credit, result);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
}
|
|
|
|
void LeSignallingManager::on_command_timeout() {
|
|
LOG_WARN("Response time out");
|
|
if (command_just_sent_.signal_id_ == kInvalidSignalId) {
|
|
LOG_ERROR("No pending command");
|
|
return;
|
|
}
|
|
switch (command_just_sent_.command_code_) {
|
|
case LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST: {
|
|
link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_,
|
|
LeCreditBasedConnectionResponseResult::NO_RESOURCES_AVAILABLE);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
handle_send_next_command();
|
|
}
|
|
|
|
void LeSignallingManager::handle_send_next_command() {
|
|
command_just_sent_.signal_id_ = kInvalidSignalId;
|
|
if (pending_commands_.empty()) {
|
|
return;
|
|
}
|
|
|
|
command_just_sent_ = pending_commands_.front();
|
|
pending_commands_.pop();
|
|
switch (command_just_sent_.command_code_) {
|
|
case LeCommandCode::LE_CREDIT_BASED_CONNECTION_REQUEST: {
|
|
auto builder = LeCreditBasedConnectionRequestBuilder::Create(
|
|
command_just_sent_.signal_id_.Value(), command_just_sent_.psm_, command_just_sent_.source_cid_,
|
|
command_just_sent_.mtu_, command_just_sent_.mps_, command_just_sent_.credits_);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
alarm_.Schedule(common::BindOnce(&LeSignallingManager::on_command_timeout, common::Unretained(this)), kTimeout);
|
|
break;
|
|
}
|
|
case LeCommandCode::DISCONNECTION_REQUEST: {
|
|
auto builder = LeDisconnectionRequestBuilder::Create(
|
|
command_just_sent_.signal_id_.Value(), command_just_sent_.destination_cid_, command_just_sent_.source_cid_);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
alarm_.Schedule(common::BindOnce(&LeSignallingManager::on_command_timeout, common::Unretained(this)), kTimeout);
|
|
break;
|
|
}
|
|
case LeCommandCode::CONNECTION_PARAMETER_UPDATE_REQUEST: {
|
|
auto builder = ConnectionParameterUpdateRequestBuilder::Create(
|
|
command_just_sent_.signal_id_.Value(),
|
|
command_just_sent_.interval_min_,
|
|
command_just_sent_.interval_max_,
|
|
command_just_sent_.peripheral_latency_,
|
|
command_just_sent_.timeout_multiplier_);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
alarm_.Schedule(common::BindOnce(&LeSignallingManager::on_command_timeout, common::Unretained(this)), kTimeout);
|
|
break;
|
|
}
|
|
default: {
|
|
LOG_WARN("Unsupported command code 0x%x", static_cast<int>(command_just_sent_.command_code_));
|
|
}
|
|
}
|
|
}
|
|
} // namespace internal
|
|
} // namespace le
|
|
} // namespace l2cap
|
|
} // namespace bluetooth
|