/* * 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 #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 GetCommandsFromPacketView(PacketView packet) { size_t curr = 0; size_t end = packet.size(); std::vector 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>(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> 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 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> 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> 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(*config)); break; } case ConfigurationOptionType::FLUSH_TIMEOUT: { auto* config = FlushTimeoutConfigurationOption::Specialize(option.get()); rsp_options.emplace_back(std::make_unique(*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(*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(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 user_channel = std::make_unique(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(); mtu_configuration->mtu_ = initial_config.incoming_mtu; auto fcs_option = std::make_unique(); 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(); 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> 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> options) { auto channel = channel_allocator_->FindChannelByCid(cid); auto& configuration_state = channel_configuration_[channel->GetCid()]; std::vector> 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(); 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(*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(*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(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> 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(option->type_)); alarm_.Cancel(); handle_send_next_command(); return; } } if (configuration_state.state_ == ChannelConfigurationState::State::WAIT_CONFIG_RSP) { std::unique_ptr user_channel = std::make_unique(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& packet) { std::vector packet_vector{packet.begin(), packet.end()}; auto raw_builder = std::make_unique(); 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& 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(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(command_just_sent_.command_code_)); } } } // namespace internal } // namespace classic } // namespace l2cap } // namespace bluetooth