blob: 3187975a8c5aee3e255525ba7f1cc656a0f366a2 [file] [log] [blame]
/*
* 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