Loading system/gd/hci/acl_fragmenter.cc +1 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ std::vector<std::unique_ptr<packet::RawBuilder>> AclFragmenter::GetFragments() { std::vector<std::unique_ptr<packet::RawBuilder>> to_return; packet::FragmentingInserter fragmenting_inserter(mtu_, std::back_insert_iterator(to_return)); packet_->Serialize(fragmenting_inserter); fragmenting_inserter.finalize(); return to_return; } Loading system/gd/l2cap/classic/cert/api.proto +27 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ service L2capModuleCert { rpc SendInformationResponse(InformationResponse) returns (SendInformationResponseResult) {} rpc FetchL2capLog(FetchL2capLogRequest) returns (stream FetchL2capLogResponse) {} rpc StopFetchingL2capLog(StopFetchingL2capLogRequest) returns (StopFetchingL2capLogResponse) {} } message L2capPacket { Loading Loading @@ -58,10 +59,27 @@ message ConnectionResponse { message SendConnectionResponseResult {} enum ChannelRetransmissionFlowControlMode { BASIC = 0; ERTM = 3; STREAM = 4; } message ChannelRetransmissionFlowControlConfig { ChannelRetransmissionFlowControlMode mode = 1; uint32 tx_window = 2; uint32 max_transmit = 3; uint32 retransmit_timeout = 4; uint32 monitor_timeout = 5; uint32 mps = 6; } message ConfigurationRequest { uint32 dcid = 1; uint32 signal_id = 2; repeated string configuration = 3; uint32 mtu = 3; ChannelRetransmissionFlowControlConfig retransmission_config = 4; bool fcs = 5; } message SendConfigurationRequestResult {} Loading @@ -69,7 +87,9 @@ message SendConfigurationRequestResult {} message ConfigurationResponse { uint32 scid = 1; uint32 signal_id = 2; repeated string configuration = 3; uint32 mtu = 3; ChannelRetransmissionFlowControlConfig retransmission_config = 4; bool fcs = 5; } message SendConfigurationResponseResult {} Loading Loading @@ -107,6 +127,7 @@ message InformationResponse { InformationRequestType type = 1; uint32 data = 2; uint32 signal_id = 3; uint32 information_value = 4; } message SendInformationResponseResult {} Loading Loading @@ -153,3 +174,7 @@ message FetchL2capLogResponse { LinkDown link_down = 21; } } message StopFetchingL2capLogRequest {} message StopFetchingL2capLogResponse {} system/gd/l2cap/classic/cert/cert.cc +56 −3 Original line number Diff line number Diff line Loading @@ -105,7 +105,22 @@ class L2capModuleCertService : public L2capModuleCert::Service { ::grpc::Status SendConfigurationRequest( ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::ConfigurationRequest* request, ::bluetooth::l2cap::classic::cert::SendConfigurationRequestResult* response) override { auto builder = ConfigurationRequestBuilder::Create(request->signal_id(), request->dcid(), Continuation::END, {}); std::vector<std::unique_ptr<ConfigurationOption>> config; if (request->retransmission_config().mode() == ChannelRetransmissionFlowControlMode::ERTM) { auto option = std::make_unique<RetransmissionAndFlowControlConfigurationOption>(); option->mode_ = RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION; option->tx_window_size_ = 10; option->max_transmit_ = 20; option->retransmission_time_out_ = 2000; option->monitor_time_out_ = 12000; option->maximum_pdu_size_ = 1010; config.push_back(std::move(option)); auto no_fcs = std::make_unique<FrameCheckSequenceOption>(); no_fcs->fcs_type_ = FcsType::NO_FCS; config.push_back(std::move(no_fcs)); } auto builder = ConfigurationRequestBuilder::Create(request->signal_id(), request->dcid(), Continuation::END, std::move(config)); auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder)); outgoing_packet_queue_.push(std::move(l2cap_builder)); send_packet_from_queue(); Loading @@ -115,8 +130,19 @@ class L2capModuleCertService : public L2capModuleCert::Service { ::grpc::Status SendConfigurationResponse( ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::ConfigurationResponse* request, ::bluetooth::l2cap::classic::cert::SendConfigurationResponseResult* response) override { std::vector<std::unique_ptr<ConfigurationOption>> config; if (request->retransmission_config().mode() == ChannelRetransmissionFlowControlMode::ERTM) { auto option = std::make_unique<RetransmissionAndFlowControlConfigurationOption>(); option->mode_ = RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION; option->tx_window_size_ = 10; option->max_transmit_ = 20; option->retransmission_time_out_ = 2000; option->monitor_time_out_ = 12000; option->maximum_pdu_size_ = 1010; config.push_back(std::move(option)); } auto builder = ConfigurationResponseBuilder::Create(request->signal_id(), request->scid(), Continuation::END, ConfigurationResponseResult::SUCCESS, {}); ConfigurationResponseResult::SUCCESS, std::move(config)); auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder)); outgoing_packet_queue_.push(std::move(l2cap_builder)); send_packet_from_queue(); Loading Loading @@ -224,7 +250,8 @@ class L2capModuleCertService : public L2capModuleCert::Service { ::grpc::Status FetchL2capLog(::grpc::ServerContext* context, const FetchL2capLogRequest* request, ::grpc::ServerWriter<FetchL2capLogResponse>* writer) override { while (!context->IsCancelled()) { fetching_l2cap_log_ = true; while (!context->IsCancelled() && fetching_l2cap_log_) { if (!l2cap_log_.empty()) { auto& response = l2cap_log_.front(); writer->Write(response); Loading @@ -241,6 +268,16 @@ class L2capModuleCertService : public L2capModuleCert::Service { } return ::grpc::Status::OK; } ::grpc::Status StopFetchingL2capLog( ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::StopFetchingL2capLogRequest* request, ::bluetooth::l2cap::classic::cert::StopFetchingL2capLogResponse* response) override { fetching_l2cap_log_ = false; l2cap_log_cv_.notify_one(); return ::grpc::Status::OK; } bool fetching_l2cap_log_ = false; std::mutex l2cap_log_mutex_; std::queue<FetchL2capLogResponse> l2cap_log_; std::condition_variable l2cap_log_cv_; Loading Loading @@ -393,15 +430,31 @@ class L2capModuleCertService : public L2capModuleCert::Service { auto type = information_response_view.GetInfoType(); switch (type) { case InformationRequestInfoType::CONNECTIONLESS_MTU: { auto view = InformationResponseConnectionlessMtuView::Create(information_response_view); if (!view.IsValid()) { return; } log_response.mutable_information_response()->set_type(InformationRequestType::CONNECTIONLESS_MTU); log_response.mutable_information_response()->set_information_value(view.GetConnectionlessMtu()); break; } case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: { auto view = InformationResponseExtendedFeaturesView::Create(information_response_view); if (!view.IsValid()) { return; } log_response.mutable_information_response()->set_type(InformationRequestType::EXTENDED_FEATURES); int mask = view.GetEnhancedRetransmissionMode() << 3 | view.GetFcsOption() << 5; log_response.mutable_information_response()->set_information_value(mask); break; } case InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED: { auto view = InformationResponseFixedChannelsView::Create(information_response_view); if (!view.IsValid()) { return; } log_response.mutable_information_response()->set_type(InformationRequestType::FIXED_CHANNELS); log_response.mutable_information_response()->set_information_value(view.GetFixedChannels()); break; } } Loading system/gd/l2cap/classic/cert/simple_l2cap_test.py +85 −9 Original line number Diff line number Diff line Loading @@ -74,6 +74,9 @@ def is_information_response(log): def is_command_reject(log): return log.HasField("command_reject") def basic_frame_to_enhanced_information_frame(information_payload): return information_payload[2:] class SimpleL2capTest(GdBaseTestClass): def setup_test(self): self.device_under_test = self.gd_devices[0] Loading Loading @@ -105,21 +108,30 @@ class SimpleL2capTest(GdBaseTestClass): log_event_handler = EventHandler() self.next_scid = 0x40 self.scid_dcid_map = {} self.retransmission_mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.BASIC def handle_connection_request(log): log = log.connection_request self.cert_device.l2cap.SendConnectionResponse(l2cap_cert_pb2.ConnectionResponse(dcid=self.next_scid,scid=log.scid, signal_id=log.signal_id)) self.scid_dcid_map[self.next_scid] = log.scid self.next_scid += 1 self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest(dcid=log.scid, signal_id=log.signal_id+1)) self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest( dcid=log.scid, signal_id=log.signal_id+1, retransmission_config=l2cap_cert_pb2.ChannelRetransmissionFlowControlConfig( mode=self.retransmission_mode ))) log_event_handler.on(is_connection_request, handle_connection_request) def handle_connection_response(log): log = log.connection_response self.scid_dcid_map[log.scid] = log.dcid self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest(dcid=log.dcid, signal_id=log.signal_id+1)) self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest( dcid=log.dcid, signal_id=log.signal_id+1, retransmission_config=l2cap_cert_pb2.ChannelRetransmissionFlowControlConfig( mode=self.retransmission_mode ))) log_event_handler.on(is_connection_response, handle_connection_response) def handle_configuration_request(log): Loading @@ -127,8 +139,10 @@ class SimpleL2capTest(GdBaseTestClass): if log.dcid not in self.scid_dcid_map: return dcid = self.scid_dcid_map[log.dcid] self.cert_device.l2cap.SendConfigurationResponse(l2cap_cert_pb2.ConfigurationResponse(scid=dcid, signal_id=log.signal_id)) self.cert_device.l2cap.SendConfigurationResponse(l2cap_cert_pb2.ConfigurationResponse( scid=dcid, signal_id=log.signal_id, )) log_event_handler.on(is_configuration_request, handle_configuration_request) def handle_disconnection_request(log): Loading Loading @@ -168,7 +182,7 @@ class SimpleL2capTest(GdBaseTestClass): self.event_handler.execute(logs) assert self.dut_address in link_up_handled def _open_channel(self, scid=0x0101, psm=0x01): def _open_channel(self, scid=0x0101, psm=0x33): self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm)) configuration_response_handled = [] Loading @@ -184,10 +198,38 @@ class SimpleL2capTest(GdBaseTestClass): def test_connect(self): self._setup_link() self._open_channel(scid=0x0101) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) def test_connect_and_send_data_ertm_no_segmentation(self): self.retransmission_mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.ERTM self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2)) self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x33, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)) self._setup_link() scid = 0x0101 self._open_channel(scid=scid) self.device_under_test.l2cap.SendL2capPacket(l2cap_facade_pb2.L2capPacket(channel=2, payload=b"123")) data_received = [] event_handler = EventHandler() def on_data_received(log): log = log.data_packet if (log.channel == scid): log.payload = basic_frame_to_enhanced_information_frame(log.payload) data_received.append((log.channel, log.payload)) event_handler.on(lambda log : log.HasField("data_packet"), on_data_received) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) event_handler.execute(logs) assert (2, b"123") in data_received self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'*34)) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) event_handler.execute(logs) assert (scid, b"abc"*34) in data_received self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) def test_connect_and_send_data(self): self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2)) self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x01)) self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x33)) self._setup_link() scid = 0x0101 self._open_channel(scid=scid) Loading @@ -203,15 +245,17 @@ class SimpleL2capTest(GdBaseTestClass): event_handler.execute(logs) assert (2, b"123") in data_received self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=1, payload=b'abc')) self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc')) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) event_handler.execute(logs) assert (scid, b"abc") in data_received self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) def test_open_two_channels(self): self._setup_link() self._open_channel(scid=0x0101, psm=0x1) self._open_channel(scid=0x0102, psm=0x3) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) def test_accept_disconnect(self): """ Loading @@ -225,6 +269,7 @@ class SimpleL2capTest(GdBaseTestClass): def handle_disconnection_response(log): log = log.disconnection_response disconnection_response_handled.append((log.scid, log.dcid)) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_disconnection_response, handle_disconnection_response) self.cert_device.l2cap.SendDisconnectionRequest(l2cap_cert_pb2.DisconnectionRequest(scid=scid, dcid=dcid, signal_id=2)) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) Loading Loading @@ -262,11 +307,13 @@ class SimpleL2capTest(GdBaseTestClass): initiate the configuration procedure. """ psm = 1 # TODO: Use another test case self.device_under_test.l2cap.OpenChannel(l2cap_facade_pb2.OpenChannelRequest(remote=self.cert_address, psm=psm)) connection_request = [] def handle_connection_request(log): log = log.connection_request connection_request.append(log.psm) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_connection_request, handle_connection_request) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) self.event_handler.execute(logs) Loading @@ -285,6 +332,7 @@ class SimpleL2capTest(GdBaseTestClass): def handle_echo_response(log): log = log.echo_response echo_response.append(log.signal_id) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_echo_response, handle_echo_response) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) self.event_handler.execute(logs) Loading @@ -303,6 +351,7 @@ class SimpleL2capTest(GdBaseTestClass): def handle_command_reject(log): log = log.command_reject command_reject.append(log.signal_id) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_command_reject, handle_command_reject) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) self.event_handler.execute(logs) Loading @@ -321,7 +370,34 @@ class SimpleL2capTest(GdBaseTestClass): def handle_info_response(log): log = log.information_response info_response.append((log.signal_id, log.type)) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_information_response, handle_info_response) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) self.event_handler.execute(logs) assert (signal_id, l2cap_cert_pb2.InformationRequestType.FIXED_CHANNELS) in info_response def test_extended_feature_info_response_ertm(self): """ L2CAP/EXF/BV-01-C [Extended Features Information Response for Enhanced Retransmission Mode] """ self._setup_link() signal_id = 3 self.cert_device.l2cap.SendInformationRequest( l2cap_cert_pb2.InformationRequest( type=l2cap_cert_pb2.InformationRequestType.EXTENDED_FEATURES, signal_id=signal_id)) info_response = [] def handle_info_response(log): log = log.information_response info_response.append((log.signal_id, log.type, log.information_value)) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_information_response, handle_info_response) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) self.event_handler.execute(logs) expected_log_type = l2cap_cert_pb2.InformationRequestType.EXTENDED_FEATURES expected_mask = 1 << 3 assert len(info_response) == 1 assert info_response[0][0] == signal_id assert info_response[0][1] == expected_log_type assert info_response[0][2] | expected_mask == expected_mask system/gd/l2cap/classic/dynamic_channel_configuration_option.h 0 → 100644 +48 −0 Original line number Diff line number Diff line /* * 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. */ #pragma once #include "l2cap/mtu.h" namespace bluetooth { namespace l2cap { namespace classic { /** * Configuration Option specified by L2CAP Channel user on a dynamic channel. L2CAP module will configure the channel * based on user provided option. */ struct DynamicChannelConfigurationOption { enum class RetransmissionAndFlowControlMode { L2CAP_BASIC, ENHANCED_RETRANSMISSION, }; /** * Retransmission and flow control mode. Currently L2CAP_BASIC and ENHANCED_RETRANSMISSION. * If the remote doesn't support a mode, it might fall back to basic, as this is a negotiable option. */ RetransmissionAndFlowControlMode channel_mode = RetransmissionAndFlowControlMode::L2CAP_BASIC; /** * Maximum SDU size that the L2CAP Channel user is able to process. */ Mtu incoming_mtu = kDefaultClassicMtu; }; } // namespace classic } // namespace l2cap } // namespace bluetooth Loading
system/gd/hci/acl_fragmenter.cc +1 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ std::vector<std::unique_ptr<packet::RawBuilder>> AclFragmenter::GetFragments() { std::vector<std::unique_ptr<packet::RawBuilder>> to_return; packet::FragmentingInserter fragmenting_inserter(mtu_, std::back_insert_iterator(to_return)); packet_->Serialize(fragmenting_inserter); fragmenting_inserter.finalize(); return to_return; } Loading
system/gd/l2cap/classic/cert/api.proto +27 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ service L2capModuleCert { rpc SendInformationResponse(InformationResponse) returns (SendInformationResponseResult) {} rpc FetchL2capLog(FetchL2capLogRequest) returns (stream FetchL2capLogResponse) {} rpc StopFetchingL2capLog(StopFetchingL2capLogRequest) returns (StopFetchingL2capLogResponse) {} } message L2capPacket { Loading Loading @@ -58,10 +59,27 @@ message ConnectionResponse { message SendConnectionResponseResult {} enum ChannelRetransmissionFlowControlMode { BASIC = 0; ERTM = 3; STREAM = 4; } message ChannelRetransmissionFlowControlConfig { ChannelRetransmissionFlowControlMode mode = 1; uint32 tx_window = 2; uint32 max_transmit = 3; uint32 retransmit_timeout = 4; uint32 monitor_timeout = 5; uint32 mps = 6; } message ConfigurationRequest { uint32 dcid = 1; uint32 signal_id = 2; repeated string configuration = 3; uint32 mtu = 3; ChannelRetransmissionFlowControlConfig retransmission_config = 4; bool fcs = 5; } message SendConfigurationRequestResult {} Loading @@ -69,7 +87,9 @@ message SendConfigurationRequestResult {} message ConfigurationResponse { uint32 scid = 1; uint32 signal_id = 2; repeated string configuration = 3; uint32 mtu = 3; ChannelRetransmissionFlowControlConfig retransmission_config = 4; bool fcs = 5; } message SendConfigurationResponseResult {} Loading Loading @@ -107,6 +127,7 @@ message InformationResponse { InformationRequestType type = 1; uint32 data = 2; uint32 signal_id = 3; uint32 information_value = 4; } message SendInformationResponseResult {} Loading Loading @@ -153,3 +174,7 @@ message FetchL2capLogResponse { LinkDown link_down = 21; } } message StopFetchingL2capLogRequest {} message StopFetchingL2capLogResponse {}
system/gd/l2cap/classic/cert/cert.cc +56 −3 Original line number Diff line number Diff line Loading @@ -105,7 +105,22 @@ class L2capModuleCertService : public L2capModuleCert::Service { ::grpc::Status SendConfigurationRequest( ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::ConfigurationRequest* request, ::bluetooth::l2cap::classic::cert::SendConfigurationRequestResult* response) override { auto builder = ConfigurationRequestBuilder::Create(request->signal_id(), request->dcid(), Continuation::END, {}); std::vector<std::unique_ptr<ConfigurationOption>> config; if (request->retransmission_config().mode() == ChannelRetransmissionFlowControlMode::ERTM) { auto option = std::make_unique<RetransmissionAndFlowControlConfigurationOption>(); option->mode_ = RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION; option->tx_window_size_ = 10; option->max_transmit_ = 20; option->retransmission_time_out_ = 2000; option->monitor_time_out_ = 12000; option->maximum_pdu_size_ = 1010; config.push_back(std::move(option)); auto no_fcs = std::make_unique<FrameCheckSequenceOption>(); no_fcs->fcs_type_ = FcsType::NO_FCS; config.push_back(std::move(no_fcs)); } auto builder = ConfigurationRequestBuilder::Create(request->signal_id(), request->dcid(), Continuation::END, std::move(config)); auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder)); outgoing_packet_queue_.push(std::move(l2cap_builder)); send_packet_from_queue(); Loading @@ -115,8 +130,19 @@ class L2capModuleCertService : public L2capModuleCert::Service { ::grpc::Status SendConfigurationResponse( ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::ConfigurationResponse* request, ::bluetooth::l2cap::classic::cert::SendConfigurationResponseResult* response) override { std::vector<std::unique_ptr<ConfigurationOption>> config; if (request->retransmission_config().mode() == ChannelRetransmissionFlowControlMode::ERTM) { auto option = std::make_unique<RetransmissionAndFlowControlConfigurationOption>(); option->mode_ = RetransmissionAndFlowControlModeOption::ENHANCED_RETRANSMISSION; option->tx_window_size_ = 10; option->max_transmit_ = 20; option->retransmission_time_out_ = 2000; option->monitor_time_out_ = 12000; option->maximum_pdu_size_ = 1010; config.push_back(std::move(option)); } auto builder = ConfigurationResponseBuilder::Create(request->signal_id(), request->scid(), Continuation::END, ConfigurationResponseResult::SUCCESS, {}); ConfigurationResponseResult::SUCCESS, std::move(config)); auto l2cap_builder = BasicFrameBuilder::Create(kClassicSignallingCid, std::move(builder)); outgoing_packet_queue_.push(std::move(l2cap_builder)); send_packet_from_queue(); Loading Loading @@ -224,7 +250,8 @@ class L2capModuleCertService : public L2capModuleCert::Service { ::grpc::Status FetchL2capLog(::grpc::ServerContext* context, const FetchL2capLogRequest* request, ::grpc::ServerWriter<FetchL2capLogResponse>* writer) override { while (!context->IsCancelled()) { fetching_l2cap_log_ = true; while (!context->IsCancelled() && fetching_l2cap_log_) { if (!l2cap_log_.empty()) { auto& response = l2cap_log_.front(); writer->Write(response); Loading @@ -241,6 +268,16 @@ class L2capModuleCertService : public L2capModuleCert::Service { } return ::grpc::Status::OK; } ::grpc::Status StopFetchingL2capLog( ::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::StopFetchingL2capLogRequest* request, ::bluetooth::l2cap::classic::cert::StopFetchingL2capLogResponse* response) override { fetching_l2cap_log_ = false; l2cap_log_cv_.notify_one(); return ::grpc::Status::OK; } bool fetching_l2cap_log_ = false; std::mutex l2cap_log_mutex_; std::queue<FetchL2capLogResponse> l2cap_log_; std::condition_variable l2cap_log_cv_; Loading Loading @@ -393,15 +430,31 @@ class L2capModuleCertService : public L2capModuleCert::Service { auto type = information_response_view.GetInfoType(); switch (type) { case InformationRequestInfoType::CONNECTIONLESS_MTU: { auto view = InformationResponseConnectionlessMtuView::Create(information_response_view); if (!view.IsValid()) { return; } log_response.mutable_information_response()->set_type(InformationRequestType::CONNECTIONLESS_MTU); log_response.mutable_information_response()->set_information_value(view.GetConnectionlessMtu()); break; } case InformationRequestInfoType::EXTENDED_FEATURES_SUPPORTED: { auto view = InformationResponseExtendedFeaturesView::Create(information_response_view); if (!view.IsValid()) { return; } log_response.mutable_information_response()->set_type(InformationRequestType::EXTENDED_FEATURES); int mask = view.GetEnhancedRetransmissionMode() << 3 | view.GetFcsOption() << 5; log_response.mutable_information_response()->set_information_value(mask); break; } case InformationRequestInfoType::FIXED_CHANNELS_SUPPORTED: { auto view = InformationResponseFixedChannelsView::Create(information_response_view); if (!view.IsValid()) { return; } log_response.mutable_information_response()->set_type(InformationRequestType::FIXED_CHANNELS); log_response.mutable_information_response()->set_information_value(view.GetFixedChannels()); break; } } Loading
system/gd/l2cap/classic/cert/simple_l2cap_test.py +85 −9 Original line number Diff line number Diff line Loading @@ -74,6 +74,9 @@ def is_information_response(log): def is_command_reject(log): return log.HasField("command_reject") def basic_frame_to_enhanced_information_frame(information_payload): return information_payload[2:] class SimpleL2capTest(GdBaseTestClass): def setup_test(self): self.device_under_test = self.gd_devices[0] Loading Loading @@ -105,21 +108,30 @@ class SimpleL2capTest(GdBaseTestClass): log_event_handler = EventHandler() self.next_scid = 0x40 self.scid_dcid_map = {} self.retransmission_mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.BASIC def handle_connection_request(log): log = log.connection_request self.cert_device.l2cap.SendConnectionResponse(l2cap_cert_pb2.ConnectionResponse(dcid=self.next_scid,scid=log.scid, signal_id=log.signal_id)) self.scid_dcid_map[self.next_scid] = log.scid self.next_scid += 1 self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest(dcid=log.scid, signal_id=log.signal_id+1)) self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest( dcid=log.scid, signal_id=log.signal_id+1, retransmission_config=l2cap_cert_pb2.ChannelRetransmissionFlowControlConfig( mode=self.retransmission_mode ))) log_event_handler.on(is_connection_request, handle_connection_request) def handle_connection_response(log): log = log.connection_response self.scid_dcid_map[log.scid] = log.dcid self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest(dcid=log.dcid, signal_id=log.signal_id+1)) self.cert_device.l2cap.SendConfigurationRequest(l2cap_cert_pb2.ConfigurationRequest( dcid=log.dcid, signal_id=log.signal_id+1, retransmission_config=l2cap_cert_pb2.ChannelRetransmissionFlowControlConfig( mode=self.retransmission_mode ))) log_event_handler.on(is_connection_response, handle_connection_response) def handle_configuration_request(log): Loading @@ -127,8 +139,10 @@ class SimpleL2capTest(GdBaseTestClass): if log.dcid not in self.scid_dcid_map: return dcid = self.scid_dcid_map[log.dcid] self.cert_device.l2cap.SendConfigurationResponse(l2cap_cert_pb2.ConfigurationResponse(scid=dcid, signal_id=log.signal_id)) self.cert_device.l2cap.SendConfigurationResponse(l2cap_cert_pb2.ConfigurationResponse( scid=dcid, signal_id=log.signal_id, )) log_event_handler.on(is_configuration_request, handle_configuration_request) def handle_disconnection_request(log): Loading Loading @@ -168,7 +182,7 @@ class SimpleL2capTest(GdBaseTestClass): self.event_handler.execute(logs) assert self.dut_address in link_up_handled def _open_channel(self, scid=0x0101, psm=0x01): def _open_channel(self, scid=0x0101, psm=0x33): self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=psm)) configuration_response_handled = [] Loading @@ -184,10 +198,38 @@ class SimpleL2capTest(GdBaseTestClass): def test_connect(self): self._setup_link() self._open_channel(scid=0x0101) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) def test_connect_and_send_data_ertm_no_segmentation(self): self.retransmission_mode = l2cap_cert_pb2.ChannelRetransmissionFlowControlMode.ERTM self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2)) self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x33, retransmission_mode=l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM)) self._setup_link() scid = 0x0101 self._open_channel(scid=scid) self.device_under_test.l2cap.SendL2capPacket(l2cap_facade_pb2.L2capPacket(channel=2, payload=b"123")) data_received = [] event_handler = EventHandler() def on_data_received(log): log = log.data_packet if (log.channel == scid): log.payload = basic_frame_to_enhanced_information_frame(log.payload) data_received.append((log.channel, log.payload)) event_handler.on(lambda log : log.HasField("data_packet"), on_data_received) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) event_handler.execute(logs) assert (2, b"123") in data_received self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'*34)) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) event_handler.execute(logs) assert (scid, b"abc"*34) in data_received self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) def test_connect_and_send_data(self): self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2)) self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x01)) self.device_under_test.l2cap.SetDynamicChannel(l2cap_facade_pb2.SetEnableDynamicChannelRequest(psm=0x33)) self._setup_link() scid = 0x0101 self._open_channel(scid=scid) Loading @@ -203,15 +245,17 @@ class SimpleL2capTest(GdBaseTestClass): event_handler.execute(logs) assert (2, b"123") in data_received self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=1, payload=b'abc')) self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc')) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) event_handler.execute(logs) assert (scid, b"abc") in data_received self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) def test_open_two_channels(self): self._setup_link() self._open_channel(scid=0x0101, psm=0x1) self._open_channel(scid=0x0102, psm=0x3) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) def test_accept_disconnect(self): """ Loading @@ -225,6 +269,7 @@ class SimpleL2capTest(GdBaseTestClass): def handle_disconnection_response(log): log = log.disconnection_response disconnection_response_handled.append((log.scid, log.dcid)) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_disconnection_response, handle_disconnection_response) self.cert_device.l2cap.SendDisconnectionRequest(l2cap_cert_pb2.DisconnectionRequest(scid=scid, dcid=dcid, signal_id=2)) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) Loading Loading @@ -262,11 +307,13 @@ class SimpleL2capTest(GdBaseTestClass): initiate the configuration procedure. """ psm = 1 # TODO: Use another test case self.device_under_test.l2cap.OpenChannel(l2cap_facade_pb2.OpenChannelRequest(remote=self.cert_address, psm=psm)) connection_request = [] def handle_connection_request(log): log = log.connection_request connection_request.append(log.psm) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_connection_request, handle_connection_request) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) self.event_handler.execute(logs) Loading @@ -285,6 +332,7 @@ class SimpleL2capTest(GdBaseTestClass): def handle_echo_response(log): log = log.echo_response echo_response.append(log.signal_id) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_echo_response, handle_echo_response) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) self.event_handler.execute(logs) Loading @@ -303,6 +351,7 @@ class SimpleL2capTest(GdBaseTestClass): def handle_command_reject(log): log = log.command_reject command_reject.append(log.signal_id) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_command_reject, handle_command_reject) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) self.event_handler.execute(logs) Loading @@ -321,7 +370,34 @@ class SimpleL2capTest(GdBaseTestClass): def handle_info_response(log): log = log.information_response info_response.append((log.signal_id, log.type)) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_information_response, handle_info_response) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) self.event_handler.execute(logs) assert (signal_id, l2cap_cert_pb2.InformationRequestType.FIXED_CHANNELS) in info_response def test_extended_feature_info_response_ertm(self): """ L2CAP/EXF/BV-01-C [Extended Features Information Response for Enhanced Retransmission Mode] """ self._setup_link() signal_id = 3 self.cert_device.l2cap.SendInformationRequest( l2cap_cert_pb2.InformationRequest( type=l2cap_cert_pb2.InformationRequestType.EXTENDED_FEATURES, signal_id=signal_id)) info_response = [] def handle_info_response(log): log = log.information_response info_response.append((log.signal_id, log.type, log.information_value)) self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest()) self.event_handler.on(is_information_response, handle_info_response) logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest()) self.event_handler.execute(logs) expected_log_type = l2cap_cert_pb2.InformationRequestType.EXTENDED_FEATURES expected_mask = 1 << 3 assert len(info_response) == 1 assert info_response[0][0] == signal_id assert info_response[0][1] == expected_log_type assert info_response[0][2] | expected_mask == expected_mask
system/gd/l2cap/classic/dynamic_channel_configuration_option.h 0 → 100644 +48 −0 Original line number Diff line number Diff line /* * 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. */ #pragma once #include "l2cap/mtu.h" namespace bluetooth { namespace l2cap { namespace classic { /** * Configuration Option specified by L2CAP Channel user on a dynamic channel. L2CAP module will configure the channel * based on user provided option. */ struct DynamicChannelConfigurationOption { enum class RetransmissionAndFlowControlMode { L2CAP_BASIC, ENHANCED_RETRANSMISSION, }; /** * Retransmission and flow control mode. Currently L2CAP_BASIC and ENHANCED_RETRANSMISSION. * If the remote doesn't support a mode, it might fall back to basic, as this is a negotiable option. */ RetransmissionAndFlowControlMode channel_mode = RetransmissionAndFlowControlMode::L2CAP_BASIC; /** * Maximum SDU size that the L2CAP Channel user is able to process. */ Mtu incoming_mtu = kDefaultClassicMtu; }; } // namespace classic } // namespace l2cap } // namespace bluetooth