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.
975 lines
42 KiB
975 lines
42 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/classic/internal/signalling_manager.h"
|
|
|
|
#include <chrono>
|
|
|
|
#include "common/bind.h"
|
|
#include "l2cap/classic/internal/channel_configuration_state.h"
|
|
#include "l2cap/classic/internal/link.h"
|
|
#include "l2cap/internal/data_pipeline_manager.h"
|
|
#include "l2cap/l2cap_packets.h"
|
|
#include "os/log.h"
|
|
#include "packet/raw_builder.h"
|
|
|
|
namespace bluetooth {
|
|
namespace l2cap {
|
|
namespace classic {
|
|
namespace internal {
|
|
static constexpr auto kTimeout = std::chrono::seconds(3);
|
|
|
|
static std::vector<ControlView> GetCommandsFromPacketView(PacketView<kLittleEndian> packet) {
|
|
size_t curr = 0;
|
|
size_t end = packet.size();
|
|
std::vector<ControlView> result;
|
|
while (curr < end) {
|
|
auto sub_view = packet.GetLittleEndianSubview(curr, end);
|
|
auto control = ControlView::Create(sub_view);
|
|
if (!control.IsValid()) {
|
|
return {};
|
|
}
|
|
result.push_back(control);
|
|
curr += 1 + 1 + 2 + control.GetPayload().size();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ClassicSignallingManager::ClassicSignallingManager(os::Handler* handler, Link* link,
|
|
l2cap::internal::DataPipelineManager* data_pipeline_manager,
|
|
DynamicChannelServiceManagerImpl* dynamic_service_manager,
|
|
l2cap::internal::DynamicChannelAllocator* channel_allocator,
|
|
FixedChannelServiceManagerImpl* fixed_service_manager)
|
|
: handler_(handler), link_(link), data_pipeline_manager_(data_pipeline_manager),
|
|
dynamic_service_manager_(dynamic_service_manager), channel_allocator_(channel_allocator),
|
|
fixed_service_manager_(fixed_service_manager), alarm_(handler) {
|
|
ASSERT(handler_ != nullptr);
|
|
ASSERT(link_ != nullptr);
|
|
signalling_channel_ = link_->AllocateFixedChannel(kClassicSignallingCid);
|
|
signalling_channel_->GetQueueUpEnd()->RegisterDequeue(
|
|
handler_, common::Bind(&ClassicSignallingManager::on_incoming_packet, common::Unretained(this)));
|
|
enqueue_buffer_ =
|
|
std::make_unique<os::EnqueueBuffer<packet::BasePacketBuilder>>(signalling_channel_->GetQueueUpEnd());
|
|
}
|
|
|
|
ClassicSignallingManager::~ClassicSignallingManager() {
|
|
alarm_.Cancel();
|
|
signalling_channel_->GetQueueUpEnd()->UnregisterDequeue();
|
|
signalling_channel_ = nullptr;
|
|
enqueue_buffer_->Clear();
|
|
enqueue_buffer_.reset();
|
|
}
|
|
|
|
void ClassicSignallingManager::OnCommandReject(CommandRejectView command_reject_view) {
|
|
if (command_just_sent_.signal_id_ != command_reject_view.GetIdentifier()) {
|
|
LOG_WARN("Unexpected command reject: no pending request");
|
|
return;
|
|
}
|
|
if (command_just_sent_.command_code_ == CommandCode::INFORMATION_REQUEST &&
|
|
command_just_sent_.info_type_ == InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED) {
|
|
link_->OnRemoteExtendedFeatureReceived(false, false);
|
|
}
|
|
alarm_.Cancel();
|
|
handle_send_next_command();
|
|
|
|
LOG_INFO("Command rejected");
|
|
}
|
|
|
|
void ClassicSignallingManager::SendConnectionRequest(Psm psm, Cid local_cid) {
|
|
dynamic_service_manager_->GetSecurityEnforcementInterface()->Enforce(
|
|
link_->GetDevice(),
|
|
dynamic_service_manager_->GetService(psm)->GetSecurityPolicy(),
|
|
handler_->BindOnceOn(
|
|
this,
|
|
&ClassicSignallingManager::on_security_result_for_outgoing,
|
|
SecurityEnforcementType::LINK_KEY,
|
|
psm,
|
|
local_cid));
|
|
}
|
|
|
|
void ClassicSignallingManager::on_security_result_for_outgoing(
|
|
SecurityEnforcementType type, Psm psm, Cid local_cid, bool result) {
|
|
if (enqueue_buffer_.get() == nullptr) {
|
|
LOG_ERROR("Got security result callback after deletion");
|
|
return;
|
|
}
|
|
if (!result) {
|
|
LOG_WARN("Security requirement can't be satisfied. Dropping connection request");
|
|
DynamicChannelManager::ConnectionResult connection_result{
|
|
.connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_SECURITY_BLOCK,
|
|
.hci_error = hci::ErrorCode::SUCCESS,
|
|
.l2cap_connection_response_result = ConnectionResponseResult::NO_RESOURCES_AVAILABLE,
|
|
};
|
|
link_->OnOutgoingConnectionRequestFail(local_cid, connection_result);
|
|
return;
|
|
}
|
|
if (type == SecurityEnforcementType::LINK_KEY && !link_->IsAuthenticated() &&
|
|
dynamic_service_manager_->GetService(psm)->GetSecurityPolicy() !=
|
|
SecurityPolicy::_SDP_ONLY_NO_SECURITY_WHATSOEVER_PLAINTEXT_TRANSPORT_OK) {
|
|
link_->Encrypt();
|
|
// TODO(b/171253721): If we can receive ENCRYPTION_CHANGE event, we can send command after callback is received.
|
|
}
|
|
|
|
PendingCommand pending_command = {next_signal_id_, CommandCode::CONNECTION_REQUEST, psm, local_cid, {}, {}, {}};
|
|
next_signal_id_++;
|
|
pending_commands_.push(std::move(pending_command));
|
|
if (command_just_sent_.signal_id_ == kInvalidSignalId) {
|
|
handle_send_next_command();
|
|
}
|
|
}
|
|
|
|
void ClassicSignallingManager::send_configuration_request(Cid remote_cid,
|
|
std::vector<std::unique_ptr<ConfigurationOption>> config) {
|
|
PendingCommand pending_command = {next_signal_id_, CommandCode::CONFIGURATION_REQUEST, {}, {}, remote_cid, {},
|
|
std::move(config)};
|
|
next_signal_id_++;
|
|
pending_commands_.push(std::move(pending_command));
|
|
if (command_just_sent_.signal_id_ == kInvalidSignalId) {
|
|
handle_send_next_command();
|
|
}
|
|
}
|
|
|
|
void ClassicSignallingManager::SendDisconnectionRequest(Cid local_cid, Cid remote_cid) {
|
|
PendingCommand pending_command = {
|
|
next_signal_id_, CommandCode::DISCONNECTION_REQUEST, {}, local_cid, remote_cid, {}, {}};
|
|
next_signal_id_++;
|
|
pending_commands_.push(std::move(pending_command));
|
|
if (command_just_sent_.signal_id_ == kInvalidSignalId) {
|
|
handle_send_next_command();
|
|
}
|
|
}
|
|
|
|
void ClassicSignallingManager::SendInformationRequest(InformationRequestInfoType type) {
|
|
PendingCommand pending_command = {next_signal_id_, CommandCode::INFORMATION_REQUEST, {}, {}, {}, type, {}};
|
|
next_signal_id_++;
|
|
pending_commands_.push(std::move(pending_command));
|
|
if (command_just_sent_.signal_id_ == kInvalidSignalId) {
|
|
handle_send_next_command();
|
|
}
|
|
}
|
|
|
|
void ClassicSignallingManager::SendEchoRequest(std::unique_ptr<packet::RawBuilder> payload) {
|
|
LOG_WARN("Not supported");
|
|
}
|
|
|
|
void ClassicSignallingManager::CancelAlarm() {
|
|
alarm_.Cancel();
|
|
}
|
|
|
|
void ClassicSignallingManager::OnConnectionRequest(SignalId signal_id, Psm psm, Cid remote_cid) {
|
|
if (!IsPsmValid(psm)) {
|
|
LOG_WARN("Invalid psm received from remote psm:%d remote_cid:%d", psm, remote_cid);
|
|
send_connection_response(signal_id, remote_cid, kInvalidCid, ConnectionResponseResult::PSM_NOT_SUPPORTED,
|
|
ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
|
|
return;
|
|
}
|
|
|
|
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, remote_cid, kInvalidCid, ConnectionResponseResult::INVALID_CID,
|
|
ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
|
|
return;
|
|
}
|
|
/* TODO(zachoverflow): add back in with policy
|
|
if (channel_allocator_->IsPsmUsed(psm)) {
|
|
LOG_WARN("Psm already exists");
|
|
send_connection_response(signal_id, remote_cid, kInvalidCid, ConnectionResponseResult::PSM_NOT_SUPPORTED,
|
|
ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
|
|
return;
|
|
}
|
|
*/
|
|
|
|
if (!dynamic_service_manager_->IsServiceRegistered(psm)) {
|
|
LOG_INFO("Service for this psm (%d) is not registered", psm);
|
|
send_connection_response(signal_id, remote_cid, kInvalidCid, ConnectionResponseResult::PSM_NOT_SUPPORTED,
|
|
ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
|
|
return;
|
|
}
|
|
|
|
dynamic_service_manager_->GetSecurityEnforcementInterface()->Enforce(
|
|
link_->GetDevice(),
|
|
dynamic_service_manager_->GetService(psm)->GetSecurityPolicy(),
|
|
handler_->BindOnceOn(
|
|
this, &ClassicSignallingManager::on_security_result_for_incoming, psm, remote_cid, signal_id));
|
|
}
|
|
|
|
void ClassicSignallingManager::on_security_result_for_incoming(
|
|
Psm psm, Cid remote_cid, SignalId signal_id, bool result) {
|
|
if (enqueue_buffer_.get() == nullptr) {
|
|
LOG_ERROR("Got security result callback after deletion");
|
|
return;
|
|
}
|
|
if (!result) {
|
|
send_connection_response(
|
|
signal_id,
|
|
remote_cid,
|
|
0,
|
|
ConnectionResponseResult::SECURITY_BLOCK,
|
|
ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
|
|
DynamicChannelManager::ConnectionResult connection_result{
|
|
.connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_SECURITY_BLOCK,
|
|
.hci_error = hci::ErrorCode::SUCCESS,
|
|
.l2cap_connection_response_result = ConnectionResponseResult::NO_RESOURCES_AVAILABLE,
|
|
};
|
|
link_->OnOutgoingConnectionRequestFail(0, connection_result);
|
|
}
|
|
|
|
auto new_channel = link_->AllocateDynamicChannel(psm, remote_cid);
|
|
if (new_channel == nullptr) {
|
|
LOG_WARN("Can't allocate dynamic channel");
|
|
return;
|
|
}
|
|
send_connection_response(
|
|
signal_id,
|
|
remote_cid,
|
|
new_channel->GetCid(),
|
|
ConnectionResponseResult::SUCCESS,
|
|
ConnectionResponseStatus::NO_FURTHER_INFORMATION_AVAILABLE);
|
|
|
|
link_->SendInitialConfigRequestOrQueue(new_channel->GetCid());
|
|
}
|
|
|
|
void ClassicSignallingManager::OnConnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid,
|
|
ConnectionResponseResult result, ConnectionResponseStatus status) {
|
|
if (command_just_sent_.signal_id_ != signal_id ||
|
|
command_just_sent_.command_code_ != CommandCode::CONNECTION_REQUEST) {
|
|
LOG_WARN("Unexpected response: no pending request. Expected signal id %d type %s, got %d",
|
|
command_just_sent_.signal_id_.Value(), CommandCodeText(command_just_sent_.command_code_).data(),
|
|
signal_id.Value());
|
|
return;
|
|
}
|
|
if (command_just_sent_.source_cid_ != cid) {
|
|
LOG_WARN("SCID doesn't match: expected %d, received %d", command_just_sent_.source_cid_, cid);
|
|
handle_send_next_command();
|
|
return;
|
|
}
|
|
if (result == ConnectionResponseResult::PENDING) {
|
|
alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
|
|
kTimeout);
|
|
return;
|
|
}
|
|
|
|
command_just_sent_.signal_id_ = kInvalidSignalId;
|
|
alarm_.Cancel();
|
|
if (result != ConnectionResponseResult::SUCCESS) {
|
|
DynamicChannelManager::ConnectionResult connection_result{
|
|
.connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR,
|
|
.hci_error = hci::ErrorCode::SUCCESS,
|
|
.l2cap_connection_response_result = result,
|
|
};
|
|
link_->OnOutgoingConnectionRequestFail(cid, connection_result);
|
|
handle_send_next_command();
|
|
return;
|
|
}
|
|
Psm pending_psm = command_just_sent_.psm_;
|
|
auto new_channel = link_->AllocateReservedDynamicChannel(cid, pending_psm, remote_cid);
|
|
if (new_channel == nullptr) {
|
|
LOG_WARN("Can't allocate dynamic channel");
|
|
DynamicChannelManager::ConnectionResult connection_result{
|
|
.connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR,
|
|
.hci_error = hci::ErrorCode::SUCCESS,
|
|
.l2cap_connection_response_result = ConnectionResponseResult::NO_RESOURCES_AVAILABLE,
|
|
};
|
|
link_->OnOutgoingConnectionRequestFail(cid, connection_result);
|
|
handle_send_next_command();
|
|
return;
|
|
}
|
|
|
|
link_->SendInitialConfigRequestOrQueue(cid);
|
|
}
|
|
|
|
void ClassicSignallingManager::OnConfigurationRequest(SignalId signal_id, Cid cid, Continuation is_continuation,
|
|
std::vector<std::unique_ptr<ConfigurationOption>> options) {
|
|
auto channel = channel_allocator_->FindChannelByCid(cid);
|
|
if (channel == nullptr) {
|
|
LOG_WARN("Configuration request for an unknown channel");
|
|
return;
|
|
}
|
|
|
|
auto& configuration_state = channel_configuration_[cid];
|
|
std::vector<std::unique_ptr<ConfigurationOption>> rsp_options;
|
|
ConfigurationResponseResult result = ConfigurationResponseResult::SUCCESS;
|
|
auto remote_rfc_mode = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
|
|
|
|
auto initial_config_option = dynamic_service_manager_->GetService(channel->GetPsm())->GetConfigOption();
|
|
|
|
for (auto& option : options) {
|
|
switch (option->type_) {
|
|
case ConfigurationOptionType::MTU: {
|
|
auto* config = MtuConfigurationOption::Specialize(option.get());
|
|
if (config->mtu_ < initial_config_option.minimal_remote_mtu) {
|
|
LOG_WARN("Configuration request with unacceptable MTU");
|
|
config->mtu_ = initial_config_option.minimal_remote_mtu;
|
|
result = ConfigurationResponseResult::UNACCEPTABLE_PARAMETERS;
|
|
}
|
|
rsp_options.emplace_back(std::make_unique<MtuConfigurationOption>(*config));
|
|
break;
|
|
}
|
|
case ConfigurationOptionType::FLUSH_TIMEOUT: {
|
|
auto* config = FlushTimeoutConfigurationOption::Specialize(option.get());
|
|
rsp_options.emplace_back(std::make_unique<FlushTimeoutConfigurationOption>(*config));
|
|
break;
|
|
}
|
|
case ConfigurationOptionType::RETRANSMISSION_AND_FLOW_CONTROL: {
|
|
auto* config = RetransmissionAndFlowControlConfigurationOption::Specialize(option.get());
|
|
remote_rfc_mode = config->mode_;
|
|
if (config->mode_ == RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION) {
|
|
if (config->retransmission_time_out_ == 0) {
|
|
config->retransmission_time_out_ = 2000;
|
|
}
|
|
if (config->monitor_time_out_ == 0) {
|
|
config->monitor_time_out_ = 12000;
|
|
}
|
|
}
|
|
configuration_state.remote_retransmission_and_flow_control_ = *config;
|
|
configuration_state.retransmission_and_flow_control_mode_ = config->mode_;
|
|
rsp_options.emplace_back(std::make_unique<RetransmissionAndFlowControlConfigurationOption>(*config));
|
|
break;
|
|
}
|
|
case ConfigurationOptionType::FRAME_CHECK_SEQUENCE: {
|
|
// We determine whether to use FCS or not when we send config request
|
|
break;
|
|
}
|
|
default:
|
|
if (option->is_hint_ != ConfigurationOptionIsHint::OPTION_IS_A_HINT) {
|
|
LOG_WARN("Received some unsupported configuration option: %d", static_cast<int>(option->type_));
|
|
auto response =
|
|
ConfigurationResponseBuilder::Create(signal_id.Value(), channel->GetRemoteCid(), is_continuation,
|
|
ConfigurationResponseResult::UNKNOWN_OPTIONS, {});
|
|
enqueue_buffer_->Enqueue(std::move(response), handler_);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (remote_rfc_mode == RetransmissionAndFlowControlModeOption::L2CAP_BASIC &&
|
|
initial_config_option.channel_mode ==
|
|
DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION) {
|
|
LOG_WARN("ERTM mandatory not allow mode configuration, disconnect channel.");
|
|
SendDisconnectionRequest(channel->GetCid(), channel->GetRemoteCid());
|
|
return;
|
|
}
|
|
|
|
if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_REQ) {
|
|
std::unique_ptr<DynamicChannel> user_channel = std::make_unique<DynamicChannel>(channel, handler_);
|
|
if (channel->local_initiated_) {
|
|
link_->NotifyChannelCreation(cid, std::move(user_channel));
|
|
} else {
|
|
dynamic_service_manager_->GetService(channel->GetPsm())->NotifyChannelCreation(std::move(user_channel));
|
|
}
|
|
configuration_state.state_ = ChannelConfigurationState::State::CONFIGURED;
|
|
data_pipeline_manager_->AttachChannel(cid, channel, l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
|
|
data_pipeline_manager_->UpdateClassicConfiguration(cid, configuration_state);
|
|
} else if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_REQ_RSP) {
|
|
configuration_state.state_ = ChannelConfigurationState::State::WAIT_CONFIG_RSP;
|
|
}
|
|
|
|
auto response = ConfigurationResponseBuilder::Create(signal_id.Value(), channel->GetRemoteCid(), is_continuation,
|
|
result, std::move(rsp_options));
|
|
enqueue_buffer_->Enqueue(std::move(response), handler_);
|
|
}
|
|
|
|
void ClassicSignallingManager::SendInitialConfigRequest(Cid local_cid) {
|
|
auto channel = channel_allocator_->FindChannelByCid(local_cid);
|
|
auto psm = channel->GetPsm();
|
|
auto& configuration_state = channel_configuration_[local_cid];
|
|
auto* service = dynamic_service_manager_->GetService(psm);
|
|
auto initial_config = service->GetConfigOption();
|
|
|
|
auto mtu_configuration = std::make_unique<MtuConfigurationOption>();
|
|
mtu_configuration->mtu_ = initial_config.incoming_mtu;
|
|
|
|
auto fcs_option = std::make_unique<FrameCheckSequenceOption>();
|
|
fcs_option->fcs_type_ = FcsType::NO_FCS;
|
|
configuration_state.fcs_type_ = FcsType::NO_FCS;
|
|
if (link_->GetRemoteSupportsFcs()) {
|
|
fcs_option->fcs_type_ = FcsType::DEFAULT;
|
|
configuration_state.fcs_type_ = FcsType::DEFAULT;
|
|
}
|
|
|
|
auto retransmission_flow_control_configuration = std::make_unique<RetransmissionAndFlowControlConfigurationOption>();
|
|
switch (initial_config.channel_mode) {
|
|
case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC:
|
|
retransmission_flow_control_configuration->mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
|
|
configuration_state.retransmission_and_flow_control_mode_ = RetransmissionAndFlowControlModeOption::L2CAP_BASIC;
|
|
break;
|
|
case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION:
|
|
case DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION_OPTIONAL:
|
|
retransmission_flow_control_configuration->mode_ =
|
|
RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
|
|
configuration_state.retransmission_and_flow_control_mode_ =
|
|
RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION;
|
|
// TODO: Decide where to put initial values
|
|
retransmission_flow_control_configuration->tx_window_size_ = 10;
|
|
retransmission_flow_control_configuration->max_transmit_ = 20;
|
|
retransmission_flow_control_configuration->retransmission_time_out_ = 2000;
|
|
retransmission_flow_control_configuration->monitor_time_out_ = 12000;
|
|
retransmission_flow_control_configuration->maximum_pdu_size_ = 1010;
|
|
break;
|
|
}
|
|
configuration_state.local_retransmission_and_flow_control_ = *retransmission_flow_control_configuration;
|
|
|
|
std::vector<std::unique_ptr<ConfigurationOption>> config;
|
|
config.emplace_back(std::move(mtu_configuration));
|
|
if (initial_config.channel_mode != DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC) {
|
|
config.emplace_back(std::move(retransmission_flow_control_configuration));
|
|
config.emplace_back(std::move(fcs_option));
|
|
}
|
|
send_configuration_request(channel->GetRemoteCid(), std::move(config));
|
|
}
|
|
|
|
void ClassicSignallingManager::negotiate_configuration(Cid cid, Continuation is_continuation,
|
|
std::vector<std::unique_ptr<ConfigurationOption>> options) {
|
|
auto channel = channel_allocator_->FindChannelByCid(cid);
|
|
auto& configuration_state = channel_configuration_[channel->GetCid()];
|
|
std::vector<std::unique_ptr<ConfigurationOption>> negotiation_config;
|
|
bool can_negotiate = false;
|
|
for (auto& option : options) {
|
|
switch (option->type_) {
|
|
case ConfigurationOptionType::MTU: {
|
|
// MTU is non-negotiable option. Use default mtu size
|
|
auto mtu_configuration = std::make_unique<MtuConfigurationOption>();
|
|
mtu_configuration->mtu_ = kDefaultClassicMtu;
|
|
negotiation_config.emplace_back(std::move(mtu_configuration));
|
|
can_negotiate = true;
|
|
break;
|
|
}
|
|
case ConfigurationOptionType::FRAME_CHECK_SEQUENCE:
|
|
case ConfigurationOptionType::FLUSH_TIMEOUT: {
|
|
// TODO: Handle these two configuration options negotiation.
|
|
can_negotiate = true;
|
|
break;
|
|
}
|
|
case ConfigurationOptionType::RETRANSMISSION_AND_FLOW_CONTROL: {
|
|
auto* config = RetransmissionAndFlowControlConfigurationOption::Specialize(option.get());
|
|
if (config->mode_ == RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION) {
|
|
configuration_state.retransmission_and_flow_control_mode_ = config->mode_;
|
|
configuration_state.local_retransmission_and_flow_control_ = *config;
|
|
negotiation_config.emplace_back(std::make_unique<RetransmissionAndFlowControlConfigurationOption>(*config));
|
|
} else if (config->mode_ == RetransmissionAndFlowControlModeOption::L2CAP_BASIC) {
|
|
auto initial_config_option = dynamic_service_manager_->GetService(channel->GetPsm())->GetConfigOption();
|
|
if (initial_config_option.channel_mode ==
|
|
DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION) {
|
|
// ERTM mandatory is not allow negotiating of retransmission and flow control mode, disconnect channel
|
|
SendDisconnectionRequest(channel->GetCid(), channel->GetRemoteCid());
|
|
return;
|
|
} else if (initial_config_option.channel_mode ==
|
|
DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::
|
|
ENHANCED_RETRANSMISSION_OPTIONAL) {
|
|
can_negotiate = true;
|
|
negotiation_config.emplace_back(std::make_unique<RetransmissionAndFlowControlConfigurationOption>(*config));
|
|
}
|
|
} else {
|
|
// Not support other retransmission and flow control mode, disconnect channel.
|
|
SendDisconnectionRequest(channel->GetCid(), channel->GetRemoteCid());
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
LOG_WARN("Received some unsupported configuration option: %d", static_cast<int>(option->type_));
|
|
return;
|
|
}
|
|
}
|
|
if (can_negotiate) {
|
|
send_configuration_request(channel->GetRemoteCid(), std::move(negotiation_config));
|
|
} else {
|
|
LOG_INFO("No suggested parameter received");
|
|
}
|
|
}
|
|
|
|
void ClassicSignallingManager::OnConfigurationResponse(SignalId signal_id, Cid cid, Continuation is_continuation,
|
|
ConfigurationResponseResult result,
|
|
std::vector<std::unique_ptr<ConfigurationOption>> options) {
|
|
if (command_just_sent_.signal_id_ != signal_id ||
|
|
command_just_sent_.command_code_ != CommandCode::CONFIGURATION_REQUEST) {
|
|
LOG_WARN("Unexpected response: no pending request. Expected signal id %d type %s, got %d",
|
|
command_just_sent_.signal_id_.Value(), CommandCodeText(command_just_sent_.command_code_).data(),
|
|
signal_id.Value());
|
|
return;
|
|
}
|
|
|
|
auto channel = channel_allocator_->FindChannelByCid(cid);
|
|
if (channel == nullptr) {
|
|
LOG_WARN("Configuration request for an unknown channel");
|
|
handle_send_next_command();
|
|
return;
|
|
}
|
|
|
|
switch (result) {
|
|
default:
|
|
case ConfigurationResponseResult::REJECTED:
|
|
case ConfigurationResponseResult::UNKNOWN_OPTIONS:
|
|
case ConfigurationResponseResult::FLOW_SPEC_REJECTED:
|
|
LOG_WARN("Configuration response not SUCCESS: %s", ConfigurationResponseResultText(result).c_str());
|
|
alarm_.Cancel();
|
|
handle_send_next_command();
|
|
return;
|
|
|
|
case ConfigurationResponseResult::PENDING:
|
|
alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
|
|
kTimeout);
|
|
return;
|
|
|
|
case ConfigurationResponseResult::UNACCEPTABLE_PARAMETERS:
|
|
LOG_INFO("Configuration response with unacceptable parameters");
|
|
alarm_.Cancel();
|
|
negotiate_configuration(cid, is_continuation, std::move(options));
|
|
handle_send_next_command();
|
|
return;
|
|
|
|
case ConfigurationResponseResult::SUCCESS:
|
|
break;
|
|
}
|
|
auto& configuration_state = channel_configuration_[channel->GetCid()];
|
|
|
|
for (auto& option : options) {
|
|
switch (option->type_) {
|
|
case ConfigurationOptionType::MTU: {
|
|
// Since they accepted our MTU, no need to read the new value.
|
|
break;
|
|
}
|
|
case ConfigurationOptionType::FLUSH_TIMEOUT: {
|
|
break;
|
|
}
|
|
case ConfigurationOptionType::RETRANSMISSION_AND_FLOW_CONTROL: {
|
|
auto config = RetransmissionAndFlowControlConfigurationOption::Specialize(option.get());
|
|
if (configuration_state.retransmission_and_flow_control_mode_ != config->mode_) {
|
|
SendDisconnectionRequest(cid, channel->GetRemoteCid());
|
|
alarm_.Cancel();
|
|
handle_send_next_command();
|
|
return;
|
|
}
|
|
configuration_state.local_retransmission_and_flow_control_ = *config;
|
|
break;
|
|
}
|
|
case ConfigurationOptionType::FRAME_CHECK_SEQUENCE: {
|
|
configuration_state.fcs_type_ = FrameCheckSequenceOption::Specialize(option.get())->fcs_type_;
|
|
break;
|
|
}
|
|
default:
|
|
LOG_WARN("Received some unsupported configuration option: %d", static_cast<int>(option->type_));
|
|
alarm_.Cancel();
|
|
handle_send_next_command();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_RSP) {
|
|
std::unique_ptr<DynamicChannel> user_channel = std::make_unique<DynamicChannel>(channel, handler_);
|
|
if (channel->local_initiated_) {
|
|
link_->NotifyChannelCreation(cid, std::move(user_channel));
|
|
} else {
|
|
dynamic_service_manager_->GetService(channel->GetPsm())->NotifyChannelCreation(std::move(user_channel));
|
|
}
|
|
configuration_state.state_ = ChannelConfigurationState::State::CONFIGURED;
|
|
data_pipeline_manager_->AttachChannel(cid, channel, l2cap::internal::DataPipelineManager::ChannelMode::BASIC);
|
|
data_pipeline_manager_->UpdateClassicConfiguration(cid, configuration_state);
|
|
} else if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_REQ_RSP) {
|
|
configuration_state.state_ = ChannelConfigurationState::State::WAIT_CONFIG_REQ;
|
|
}
|
|
|
|
alarm_.Cancel();
|
|
handle_send_next_command();
|
|
}
|
|
|
|
void ClassicSignallingManager::OnDisconnectionRequest(SignalId signal_id, Cid cid, Cid remote_cid) {
|
|
// TODO: check cid match
|
|
auto channel = channel_allocator_->FindChannelByCid(cid);
|
|
if (channel == nullptr) {
|
|
LOG_WARN("Disconnect request for an unknown channel");
|
|
return;
|
|
}
|
|
auto builder = DisconnectionResponseBuilder::Create(signal_id.Value(), cid, remote_cid);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
channel->OnClosed(hci::ErrorCode::SUCCESS);
|
|
auto& configuration_state = channel_configuration_[channel->GetCid()];
|
|
if (configuration_state.state_ == configuration_state.CONFIGURED) {
|
|
data_pipeline_manager_->DetachChannel(cid);
|
|
}
|
|
link_->FreeDynamicChannel(cid);
|
|
channel_configuration_.erase(cid);
|
|
}
|
|
|
|
void ClassicSignallingManager::OnDisconnectionResponse(SignalId signal_id, Cid remote_cid, Cid cid) {
|
|
if (command_just_sent_.signal_id_ != signal_id ||
|
|
command_just_sent_.command_code_ != CommandCode::DISCONNECTION_REQUEST) {
|
|
LOG_WARN("Unexpected response: no pending request. Expected signal id %d type %s, got %d",
|
|
command_just_sent_.signal_id_.Value(), CommandCodeText(command_just_sent_.command_code_).data(),
|
|
signal_id.Value());
|
|
return;
|
|
}
|
|
|
|
alarm_.Cancel();
|
|
|
|
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);
|
|
auto& configuration_state = channel_configuration_[cid];
|
|
if (configuration_state.state_ == configuration_state.CONFIGURED) {
|
|
data_pipeline_manager_->DetachChannel(cid);
|
|
}
|
|
link_->FreeDynamicChannel(cid);
|
|
handle_send_next_command();
|
|
channel_configuration_.erase(cid);
|
|
}
|
|
|
|
void ClassicSignallingManager::OnEchoRequest(SignalId signal_id, const PacketView<kLittleEndian>& packet) {
|
|
std::vector<uint8_t> packet_vector{packet.begin(), packet.end()};
|
|
auto raw_builder = std::make_unique<packet::RawBuilder>();
|
|
raw_builder->AddOctets(packet_vector);
|
|
auto builder = EchoResponseBuilder::Create(signal_id.Value(), std::move(raw_builder));
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
}
|
|
|
|
void ClassicSignallingManager::OnEchoResponse(SignalId signal_id, const PacketView<kLittleEndian>& packet) {
|
|
if (command_just_sent_.signal_id_ != signal_id || command_just_sent_.command_code_ != CommandCode::ECHO_REQUEST) {
|
|
LOG_WARN("Unexpected response: no pending request. Expected signal id %d type %s, got %d",
|
|
command_just_sent_.signal_id_.Value(), CommandCodeText(command_just_sent_.command_code_).data(),
|
|
signal_id.Value());
|
|
return;
|
|
}
|
|
LOG_INFO("Echo response received");
|
|
alarm_.Cancel();
|
|
handle_send_next_command();
|
|
}
|
|
|
|
void ClassicSignallingManager::OnInformationRequest(SignalId signal_id, InformationRequestInfoType type) {
|
|
switch (type) {
|
|
case InformationRequestInfoType::CONNECTIONLESS_MTU: {
|
|
auto response = InformationResponseConnectionlessMtuBuilder::Create(
|
|
signal_id.Value(), InformationRequestResult::SUCCESS, kDefaultClassicMtu);
|
|
enqueue_buffer_->Enqueue(std::move(response), handler_);
|
|
break;
|
|
}
|
|
case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: {
|
|
auto response = InformationResponseExtendedFeaturesBuilder::Create(
|
|
signal_id.Value(), InformationRequestResult::SUCCESS, 0, 0, 0, 1 /* ERTM */, 0 /* Streaming mode */,
|
|
1 /* FCS */, 0, 1 /* Fixed Channels */, 0, 0, 0 /* COC */);
|
|
enqueue_buffer_->Enqueue(std::move(response), handler_);
|
|
break;
|
|
}
|
|
case InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED: {
|
|
auto response = InformationResponseFixedChannelsBuilder::Create(
|
|
signal_id.Value(), InformationRequestResult::SUCCESS, fixed_service_manager_->GetSupportedFixedChannelMask());
|
|
enqueue_buffer_->Enqueue(std::move(response), handler_);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClassicSignallingManager::OnInformationResponse(SignalId signal_id, const InformationResponseView& response) {
|
|
if (command_just_sent_.signal_id_ != signal_id ||
|
|
command_just_sent_.command_code_ != CommandCode::INFORMATION_REQUEST) {
|
|
LOG_WARN("Unexpected response: no pending request. Expected signal id %d type %s, got %d",
|
|
command_just_sent_.signal_id_.Value(), CommandCodeText(command_just_sent_.command_code_).data(),
|
|
signal_id.Value());
|
|
return;
|
|
}
|
|
|
|
auto type = response.GetInfoType();
|
|
switch (type) {
|
|
case InformationRequestInfoType::CONNECTIONLESS_MTU: {
|
|
auto view = InformationResponseConnectionlessMtuView::Create(response);
|
|
if (!view.IsValid()) {
|
|
LOG_WARN("Invalid InformationResponseConnectionlessMtu received");
|
|
return;
|
|
}
|
|
link_->SetRemoteConnectionlessMtu(view.GetConnectionlessMtu());
|
|
break;
|
|
}
|
|
case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: {
|
|
auto view = InformationResponseExtendedFeaturesView::Create(response);
|
|
if (!view.IsValid()) {
|
|
LOG_WARN("Invalid InformationResponseExtendedFeatures received");
|
|
return;
|
|
}
|
|
link_->OnRemoteExtendedFeatureReceived(view.GetEnhancedRetransmissionMode(), view.GetFcsOption());
|
|
// We don't care about other parameters
|
|
break;
|
|
}
|
|
case InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED: {
|
|
auto view = InformationResponseFixedChannelsView::Create(response);
|
|
if (!view.IsValid()) {
|
|
LOG_WARN("Invalid InformationResponseFixedChannel received");
|
|
return;
|
|
}
|
|
// We don't use fixed channels (connectionless or BR/EDR security) for now so we don't care
|
|
break;
|
|
}
|
|
}
|
|
|
|
alarm_.Cancel();
|
|
handle_send_next_command();
|
|
}
|
|
|
|
void ClassicSignallingManager::on_incoming_packet() {
|
|
auto packet = signalling_channel_->GetQueueUpEnd()->TryDequeue();
|
|
auto command_list = GetCommandsFromPacketView(*packet);
|
|
for (auto& command : command_list) {
|
|
handle_one_command(command);
|
|
}
|
|
}
|
|
|
|
void ClassicSignallingManager::handle_one_command(ControlView control_packet_view) {
|
|
if (!control_packet_view.IsValid()) {
|
|
LOG_WARN("Invalid signalling packet received");
|
|
return;
|
|
}
|
|
auto code = control_packet_view.GetCode();
|
|
switch (code) {
|
|
case CommandCode::COMMAND_REJECT: {
|
|
CommandRejectView command_reject_view = CommandRejectView::Create(control_packet_view);
|
|
if (!command_reject_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnCommandReject(command_reject_view);
|
|
return;
|
|
}
|
|
case CommandCode::CONNECTION_REQUEST: {
|
|
ConnectionRequestView connection_request_view = ConnectionRequestView::Create(control_packet_view);
|
|
if (!connection_request_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnConnectionRequest(control_packet_view.GetIdentifier(), connection_request_view.GetPsm(),
|
|
connection_request_view.GetSourceCid());
|
|
return;
|
|
}
|
|
case CommandCode::CONNECTION_RESPONSE: {
|
|
ConnectionResponseView connection_response_view = ConnectionResponseView::Create(control_packet_view);
|
|
if (!connection_response_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnConnectionResponse(connection_response_view.GetIdentifier(), connection_response_view.GetDestinationCid(),
|
|
connection_response_view.GetSourceCid(), connection_response_view.GetResult(),
|
|
connection_response_view.GetStatus());
|
|
return;
|
|
}
|
|
case CommandCode::CONFIGURATION_REQUEST: {
|
|
ConfigurationRequestView configuration_request_view = ConfigurationRequestView::Create(control_packet_view);
|
|
if (!configuration_request_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnConfigurationRequest(configuration_request_view.GetIdentifier(), configuration_request_view.GetDestinationCid(),
|
|
configuration_request_view.GetContinuation(), configuration_request_view.GetConfig());
|
|
return;
|
|
}
|
|
case CommandCode::CONFIGURATION_RESPONSE: {
|
|
ConfigurationResponseView configuration_response_view = ConfigurationResponseView::Create(control_packet_view);
|
|
if (!configuration_response_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnConfigurationResponse(configuration_response_view.GetIdentifier(), configuration_response_view.GetSourceCid(),
|
|
configuration_response_view.GetContinuation(), configuration_response_view.GetResult(),
|
|
configuration_response_view.GetConfig());
|
|
return;
|
|
}
|
|
case CommandCode::DISCONNECTION_REQUEST: {
|
|
DisconnectionRequestView disconnection_request_view = DisconnectionRequestView::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 CommandCode::DISCONNECTION_RESPONSE: {
|
|
DisconnectionResponseView disconnection_response_view = DisconnectionResponseView::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 CommandCode::ECHO_REQUEST: {
|
|
EchoRequestView echo_request_view = EchoRequestView::Create(control_packet_view);
|
|
if (!echo_request_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnEchoRequest(echo_request_view.GetIdentifier(), echo_request_view.GetPayload());
|
|
return;
|
|
}
|
|
case CommandCode::ECHO_RESPONSE: {
|
|
EchoResponseView echo_response_view = EchoResponseView::Create(control_packet_view);
|
|
if (!echo_response_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnEchoResponse(echo_response_view.GetIdentifier(), echo_response_view.GetPayload());
|
|
return;
|
|
}
|
|
case CommandCode::INFORMATION_REQUEST: {
|
|
InformationRequestView information_request_view = InformationRequestView::Create(control_packet_view);
|
|
if (!information_request_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnInformationRequest(information_request_view.GetIdentifier(), information_request_view.GetInfoType());
|
|
return;
|
|
}
|
|
case CommandCode::INFORMATION_RESPONSE: {
|
|
InformationResponseView information_response_view = InformationResponseView::Create(control_packet_view);
|
|
if (!information_response_view.IsValid()) {
|
|
return;
|
|
}
|
|
OnInformationResponse(information_response_view.GetIdentifier(), information_response_view);
|
|
return;
|
|
}
|
|
case CommandCode::CREDIT_BASED_CONNECTION_REQUEST: {
|
|
CreditBasedConnectionRequestView request_view = CreditBasedConnectionRequestView::Create(control_packet_view);
|
|
if (!request_view.IsValid()) {
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
case CommandCode::CREDIT_BASED_CONNECTION_RESPONSE: {
|
|
CreditBasedConnectionResponseView response_view = CreditBasedConnectionResponseView::Create(control_packet_view);
|
|
if (!response_view.IsValid()) {
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
case CommandCode::CREDIT_BASED_RECONFIGURE_REQUEST: {
|
|
CreditBasedReconfigureRequestView request_view = CreditBasedReconfigureRequestView::Create(control_packet_view);
|
|
if (!request_view.IsValid()) {
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
case CommandCode::CREDIT_BASED_RECONFIGURE_RESPONSE: {
|
|
CreditBasedReconfigureResponseView response_view =
|
|
CreditBasedReconfigureResponseView::Create(control_packet_view);
|
|
if (!response_view.IsValid()) {
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
case CommandCode::FLOW_CONTROL_CREDIT: {
|
|
FlowControlCreditView credit_view = FlowControlCreditView::Create(control_packet_view);
|
|
if (!credit_view.IsValid()) {
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
default:
|
|
LOG_WARN("Unhandled event 0x%x", static_cast<int>(code));
|
|
auto builder = CommandRejectNotUnderstoodBuilder::Create(control_packet_view.GetIdentifier());
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void ClassicSignallingManager::send_connection_response(SignalId signal_id, Cid remote_cid, Cid local_cid,
|
|
ConnectionResponseResult result,
|
|
ConnectionResponseStatus status) {
|
|
auto builder = ConnectionResponseBuilder::Create(signal_id.Value(), local_cid, remote_cid, result, status);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
}
|
|
|
|
void ClassicSignallingManager::on_command_timeout() {
|
|
LOG_WARN("Response time out");
|
|
if (command_just_sent_.signal_id_ == kInvalidSignalId) {
|
|
LOG_ERROR("No pending command");
|
|
return;
|
|
}
|
|
LOG_WARN("Response time out for %s", CommandCodeText(command_just_sent_.command_code_).c_str());
|
|
switch (command_just_sent_.command_code_) {
|
|
case CommandCode::CONNECTION_REQUEST: {
|
|
DynamicChannelManager::ConnectionResult connection_result{
|
|
.connection_result_code = DynamicChannelManager::ConnectionResultCode::FAIL_L2CAP_ERROR,
|
|
.hci_error = hci::ErrorCode::SUCCESS,
|
|
.l2cap_connection_response_result = ConnectionResponseResult::NO_RESOURCES_AVAILABLE,
|
|
};
|
|
link_->OnOutgoingConnectionRequestFail(command_just_sent_.source_cid_, connection_result);
|
|
break;
|
|
}
|
|
case CommandCode::CONFIGURATION_REQUEST: {
|
|
auto channel = channel_allocator_->FindChannelByRemoteCid(command_just_sent_.destination_cid_);
|
|
SendDisconnectionRequest(channel->GetCid(), channel->GetRemoteCid());
|
|
return;
|
|
}
|
|
case CommandCode::INFORMATION_REQUEST: {
|
|
if (command_just_sent_.info_type_ == InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED) {
|
|
link_->OnRemoteExtendedFeatureReceived(false, false);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
handle_send_next_command();
|
|
}
|
|
|
|
void ClassicSignallingManager::handle_send_next_command() {
|
|
command_just_sent_.signal_id_ = kInvalidSignalId;
|
|
if (pending_commands_.empty()) {
|
|
return;
|
|
}
|
|
command_just_sent_ = std::move(pending_commands_.front());
|
|
pending_commands_.pop();
|
|
|
|
auto signal_id = command_just_sent_.signal_id_;
|
|
auto psm = command_just_sent_.psm_;
|
|
auto source_cid = command_just_sent_.source_cid_;
|
|
auto destination_cid = command_just_sent_.destination_cid_;
|
|
auto info_type = command_just_sent_.info_type_;
|
|
auto config = std::move(command_just_sent_.config_);
|
|
switch (command_just_sent_.command_code_) {
|
|
case CommandCode::CONNECTION_REQUEST: {
|
|
auto builder = ConnectionRequestBuilder::Create(signal_id.Value(), psm, source_cid);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
|
|
kTimeout);
|
|
break;
|
|
}
|
|
case CommandCode::CONFIGURATION_REQUEST: {
|
|
auto builder =
|
|
ConfigurationRequestBuilder::Create(signal_id.Value(), destination_cid, Continuation::END, std::move(config));
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
|
|
kTimeout);
|
|
break;
|
|
}
|
|
case CommandCode::DISCONNECTION_REQUEST: {
|
|
auto builder = DisconnectionRequestBuilder::Create(signal_id.Value(), destination_cid, source_cid);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::on_command_timeout, common::Unretained(this)),
|
|
kTimeout);
|
|
break;
|
|
}
|
|
case CommandCode::INFORMATION_REQUEST: {
|
|
auto builder = InformationRequestBuilder::Create(signal_id.Value(), info_type);
|
|
enqueue_buffer_->Enqueue(std::move(builder), handler_);
|
|
alarm_.Schedule(common::BindOnce(&ClassicSignallingManager::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 classic
|
|
} // namespace l2cap
|
|
} // namespace bluetooth
|