Loading system/gd/cert/py_l2cap.py +8 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,11 @@ class PyL2capChannel(IEventStream): self._device.l2cap.CloseChannel( l2cap_facade_pb2.CloseChannelRequest(psm=self._psm)) def set_traffic_paused(self, paused): self._device.l2cap.SetTrafficPaused( l2cap_facade_pb2.SetTrafficPausedRequest( psm=self._psm, paused=paused)) class _ClassicConnectionResponseFutureWrapper(object): """ Loading Loading @@ -102,6 +107,9 @@ class PyL2cap(Closable): return _ClassicConnectionResponseFutureWrapper( response_future, self._device, psm, self._l2cap_stream) def get_channel_queue_buffer_size(self): return self._device.l2cap.GetChannelQueueDepth(empty_proto.Empty()).size class PyLeL2capFixedChannel(IEventStream): Loading system/gd/l2cap/classic/cert/l2cap_test.py +90 −0 Original line number Diff line number Diff line Loading @@ -883,6 +883,47 @@ class L2capTest(GdBaseTestClass): cert_channel.send_i_frame(tx_seq=1, req_seq=2, payload=SAMPLE_PACKET) @metadata( pts_test_id="L2CAP/ERM/BV-07-C", pts_test_name="Send S-Frame [RNR]") def test_send_s_frame_rnr(self): """ Verify the IUT sends an S-frame [RNR] when it detects local busy condition NOTE: In GD stack, we enter local busy condition if client doesn't dequeue and packets are accumulating in buffer """ self._setup_link_from_cert() config = CertL2cap.config_option_ertm( fcs=FcsType.NO_FCS, tx_window_size=10) (dut_channel, cert_channel) = self._open_channel_from_cert( mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS, req_config_options=config, rsp_config_options=config) dut_channel.send(b'abc') assertThat(cert_channel).emits( L2capMatchers.IFrame(tx_seq=0, payload=b'abc')) cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET) dut_channel.set_traffic_paused(True) buffer_size = self.dut_l2cap.get_channel_queue_buffer_size() for i in range(buffer_size): cert_channel.send_i_frame( tx_seq=i + 1, req_seq=1, payload=SAMPLE_PACKET) assertThat(cert_channel).emits( L2capMatchers.SFrame( s=l2cap_packets.SupervisoryFunction.RECEIVER_READY)) cert_channel.send_i_frame( tx_seq=buffer_size + 1, req_seq=1, payload=SAMPLE_PACKET) assertThat(cert_channel).emits( L2capMatchers.SFrame( s=l2cap_packets.SupervisoryFunction.RECEIVER_NOT_READY)) @metadata( pts_test_id="L2CAP/ERM/BV-08-C", pts_test_name="Send S-Frame [RR] with Poll Bit Set") Loading Loading @@ -1205,6 +1246,55 @@ class L2capTest(GdBaseTestClass): f=Final.POLL_RESPONSE) assertThat(cert_channel).emitsNone(L2capMatchers.IFrame(tx_seq=0)) @metadata( pts_test_id="L2CAP/ERM/BV-22-C", pts_test_name="Exit Local Busy Condition") def test_exit_local_busy_condition(self): """ Verify the IUT sends an S-frame [RR] Poll = 1 when the local busy condition is cleared NOTE: In GD stack, we enter local busy condition if client doesn't dequeue and packets are accumulating in buffer """ self._setup_link_from_cert() config = CertL2cap.config_option_ertm( fcs=FcsType.NO_FCS, tx_window_size=10) (dut_channel, cert_channel) = self._open_channel_from_cert( mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS, req_config_options=config, rsp_config_options=config) dut_channel.send(b'abc') assertThat(cert_channel).emits( L2capMatchers.IFrame(tx_seq=0, payload=b'abc')) cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET) dut_channel.set_traffic_paused(True) buffer_size = self.dut_l2cap.get_channel_queue_buffer_size() for i in range(buffer_size): cert_channel.send_i_frame( tx_seq=i + 1, req_seq=1, payload=SAMPLE_PACKET) assertThat(cert_channel).emits( L2capMatchers.SFrame( s=l2cap_packets.SupervisoryFunction.RECEIVER_READY)) cert_channel.send_i_frame( tx_seq=buffer_size + 1, req_seq=1, payload=SAMPLE_PACKET) assertThat(cert_channel).emits( L2capMatchers.SFrame( s=l2cap_packets.SupervisoryFunction.RECEIVER_NOT_READY)) dut_channel.set_traffic_paused(False) assertThat(cert_channel).emits( L2capMatchers.SFrame( s=l2cap_packets.SupervisoryFunction.RECEIVER_READY, p=l2cap_packets.Poll.POLL)) cert_channel.send_s_frame(1, f=l2cap_packets.Final.POLL_RESPONSE) @metadata( pts_test_id="L2CAP/ERM/BV-23-C", pts_test_name="Transmit I-Frames using SAR") Loading system/gd/l2cap/classic/facade.cc +41 −2 Original line number Diff line number Diff line Loading @@ -108,6 +108,27 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service return ::grpc::Status::OK; } ::grpc::Status SetTrafficPaused(::grpc::ServerContext* context, const SetTrafficPausedRequest* request, ::google::protobuf::Empty* response) override { auto psm = request->psm(); if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) { return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered"); } if (request->paused()) { dynamic_channel_helper_map_[psm]->SuspendDequeue(); } else { dynamic_channel_helper_map_[psm]->ResumeDequeue(); } return ::grpc::Status::OK; } ::grpc::Status GetChannelQueueDepth(::grpc::ServerContext* context, const ::google::protobuf::Empty* request, GetChannelQueueDepthResponse* response) override { // Use the value kChannelQueueSize (5) in internal/dynamic_channel_impl.h response->set_size(5); return ::grpc::Status::OK; } class L2capDynamicChannelHelper { public: L2capDynamicChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer, Loading @@ -124,7 +145,7 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service } ~L2capDynamicChannelHelper() { if (channel_ != nullptr) { if (dequeue_registered_) { channel_->GetQueueUpEnd()->UnregisterDequeue(); channel_ = nullptr; } Loading Loading @@ -171,6 +192,7 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service channel_->RegisterOnCloseCallback( facade_service_->facade_handler_, common::BindOnce(&L2capDynamicChannelHelper::on_close_callback, common::Unretained(this))); dequeue_registered_ = true; channel_->GetQueueUpEnd()->RegisterDequeue( facade_service_->facade_handler_, common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this))); Loading @@ -179,8 +201,10 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service void on_close_callback(hci::ErrorCode error_code) { { std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); if (dequeue_registered_.exchange(false)) { channel_->GetQueueUpEnd()->UnregisterDequeue(); } } classic::ConnectionCloseEvent event; event.mutable_remote()->set_address(channel_->GetDevice().GetAddress().ToString()); event.set_reason(static_cast<uint32_t>(error_code)); Loading @@ -188,6 +212,20 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service channel_ = nullptr; } void SuspendDequeue() { if (dequeue_registered_.exchange(false)) { channel_->GetQueueUpEnd()->UnregisterDequeue(); } } void ResumeDequeue() { if (!dequeue_registered_.exchange(true)) { channel_->GetQueueUpEnd()->RegisterDequeue( facade_service_->facade_handler_, common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this))); } } void on_connect_fail(DynamicChannelManager::ConnectionResult result) {} void on_incoming_packet() { Loading Loading @@ -237,6 +275,7 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service std::unique_ptr<DynamicChannel> channel_ = nullptr; Psm psm_; RetransmissionFlowControlMode mode_ = RetransmissionFlowControlMode::BASIC; std::atomic_bool dequeue_registered_ = false; std::condition_variable channel_open_cv_; std::mutex channel_open_cv_mutex_; }; Loading system/gd/l2cap/classic/facade.proto +14 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,11 @@ service L2capClassicModuleFacade { rpc FetchL2capData(google.protobuf.Empty) returns (stream L2capPacket) {} rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (google.protobuf.Empty) {} rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (google.protobuf.Empty) {} rpc SetTrafficPaused(SetTrafficPausedRequest) returns (google.protobuf.Empty) {} rpc GetChannelQueueDepth(google.protobuf.Empty) returns (GetChannelQueueDepthResponse) { // Get the buffer size of channel queue end for L2CAP user (how many packets we can buffer // before L2CAP user dequeues. } } message RegisterChannelRequest { Loading Loading @@ -87,3 +92,12 @@ message DynamicChannelPacket { uint32 psm = 2; bytes payload = 3; } message SetTrafficPausedRequest { bool paused = 1; uint32 psm = 2; } message GetChannelQueueDepthResponse { uint32 size = 1; } system/gd/l2cap/internal/dynamic_channel_impl.h +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,7 @@ class DynamicChannelImpl : public l2cap::internal::ChannelImpl { // Internal states bool closed_ = false; hci::ErrorCode close_reason_ = hci::ErrorCode::SUCCESS; static constexpr size_t kChannelQueueSize = 10; static constexpr size_t kChannelQueueSize = 5; common::BidiQueue<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder> channel_queue_{ kChannelQueueSize}; Loading Loading
system/gd/cert/py_l2cap.py +8 −0 Original line number Diff line number Diff line Loading @@ -49,6 +49,11 @@ class PyL2capChannel(IEventStream): self._device.l2cap.CloseChannel( l2cap_facade_pb2.CloseChannelRequest(psm=self._psm)) def set_traffic_paused(self, paused): self._device.l2cap.SetTrafficPaused( l2cap_facade_pb2.SetTrafficPausedRequest( psm=self._psm, paused=paused)) class _ClassicConnectionResponseFutureWrapper(object): """ Loading Loading @@ -102,6 +107,9 @@ class PyL2cap(Closable): return _ClassicConnectionResponseFutureWrapper( response_future, self._device, psm, self._l2cap_stream) def get_channel_queue_buffer_size(self): return self._device.l2cap.GetChannelQueueDepth(empty_proto.Empty()).size class PyLeL2capFixedChannel(IEventStream): Loading
system/gd/l2cap/classic/cert/l2cap_test.py +90 −0 Original line number Diff line number Diff line Loading @@ -883,6 +883,47 @@ class L2capTest(GdBaseTestClass): cert_channel.send_i_frame(tx_seq=1, req_seq=2, payload=SAMPLE_PACKET) @metadata( pts_test_id="L2CAP/ERM/BV-07-C", pts_test_name="Send S-Frame [RNR]") def test_send_s_frame_rnr(self): """ Verify the IUT sends an S-frame [RNR] when it detects local busy condition NOTE: In GD stack, we enter local busy condition if client doesn't dequeue and packets are accumulating in buffer """ self._setup_link_from_cert() config = CertL2cap.config_option_ertm( fcs=FcsType.NO_FCS, tx_window_size=10) (dut_channel, cert_channel) = self._open_channel_from_cert( mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS, req_config_options=config, rsp_config_options=config) dut_channel.send(b'abc') assertThat(cert_channel).emits( L2capMatchers.IFrame(tx_seq=0, payload=b'abc')) cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET) dut_channel.set_traffic_paused(True) buffer_size = self.dut_l2cap.get_channel_queue_buffer_size() for i in range(buffer_size): cert_channel.send_i_frame( tx_seq=i + 1, req_seq=1, payload=SAMPLE_PACKET) assertThat(cert_channel).emits( L2capMatchers.SFrame( s=l2cap_packets.SupervisoryFunction.RECEIVER_READY)) cert_channel.send_i_frame( tx_seq=buffer_size + 1, req_seq=1, payload=SAMPLE_PACKET) assertThat(cert_channel).emits( L2capMatchers.SFrame( s=l2cap_packets.SupervisoryFunction.RECEIVER_NOT_READY)) @metadata( pts_test_id="L2CAP/ERM/BV-08-C", pts_test_name="Send S-Frame [RR] with Poll Bit Set") Loading Loading @@ -1205,6 +1246,55 @@ class L2capTest(GdBaseTestClass): f=Final.POLL_RESPONSE) assertThat(cert_channel).emitsNone(L2capMatchers.IFrame(tx_seq=0)) @metadata( pts_test_id="L2CAP/ERM/BV-22-C", pts_test_name="Exit Local Busy Condition") def test_exit_local_busy_condition(self): """ Verify the IUT sends an S-frame [RR] Poll = 1 when the local busy condition is cleared NOTE: In GD stack, we enter local busy condition if client doesn't dequeue and packets are accumulating in buffer """ self._setup_link_from_cert() config = CertL2cap.config_option_ertm( fcs=FcsType.NO_FCS, tx_window_size=10) (dut_channel, cert_channel) = self._open_channel_from_cert( mode=RetransmissionFlowControlMode.ERTM, fcs=FcsType.NO_FCS, req_config_options=config, rsp_config_options=config) dut_channel.send(b'abc') assertThat(cert_channel).emits( L2capMatchers.IFrame(tx_seq=0, payload=b'abc')) cert_channel.send_i_frame(tx_seq=0, req_seq=1, payload=SAMPLE_PACKET) dut_channel.set_traffic_paused(True) buffer_size = self.dut_l2cap.get_channel_queue_buffer_size() for i in range(buffer_size): cert_channel.send_i_frame( tx_seq=i + 1, req_seq=1, payload=SAMPLE_PACKET) assertThat(cert_channel).emits( L2capMatchers.SFrame( s=l2cap_packets.SupervisoryFunction.RECEIVER_READY)) cert_channel.send_i_frame( tx_seq=buffer_size + 1, req_seq=1, payload=SAMPLE_PACKET) assertThat(cert_channel).emits( L2capMatchers.SFrame( s=l2cap_packets.SupervisoryFunction.RECEIVER_NOT_READY)) dut_channel.set_traffic_paused(False) assertThat(cert_channel).emits( L2capMatchers.SFrame( s=l2cap_packets.SupervisoryFunction.RECEIVER_READY, p=l2cap_packets.Poll.POLL)) cert_channel.send_s_frame(1, f=l2cap_packets.Final.POLL_RESPONSE) @metadata( pts_test_id="L2CAP/ERM/BV-23-C", pts_test_name="Transmit I-Frames using SAR") Loading
system/gd/l2cap/classic/facade.cc +41 −2 Original line number Diff line number Diff line Loading @@ -108,6 +108,27 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service return ::grpc::Status::OK; } ::grpc::Status SetTrafficPaused(::grpc::ServerContext* context, const SetTrafficPausedRequest* request, ::google::protobuf::Empty* response) override { auto psm = request->psm(); if (dynamic_channel_helper_map_.find(request->psm()) == dynamic_channel_helper_map_.end()) { return ::grpc::Status(::grpc::StatusCode::FAILED_PRECONDITION, "Psm not registered"); } if (request->paused()) { dynamic_channel_helper_map_[psm]->SuspendDequeue(); } else { dynamic_channel_helper_map_[psm]->ResumeDequeue(); } return ::grpc::Status::OK; } ::grpc::Status GetChannelQueueDepth(::grpc::ServerContext* context, const ::google::protobuf::Empty* request, GetChannelQueueDepthResponse* response) override { // Use the value kChannelQueueSize (5) in internal/dynamic_channel_impl.h response->set_size(5); return ::grpc::Status::OK; } class L2capDynamicChannelHelper { public: L2capDynamicChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer, Loading @@ -124,7 +145,7 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service } ~L2capDynamicChannelHelper() { if (channel_ != nullptr) { if (dequeue_registered_) { channel_->GetQueueUpEnd()->UnregisterDequeue(); channel_ = nullptr; } Loading Loading @@ -171,6 +192,7 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service channel_->RegisterOnCloseCallback( facade_service_->facade_handler_, common::BindOnce(&L2capDynamicChannelHelper::on_close_callback, common::Unretained(this))); dequeue_registered_ = true; channel_->GetQueueUpEnd()->RegisterDequeue( facade_service_->facade_handler_, common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this))); Loading @@ -179,8 +201,10 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service void on_close_callback(hci::ErrorCode error_code) { { std::unique_lock<std::mutex> lock(channel_open_cv_mutex_); if (dequeue_registered_.exchange(false)) { channel_->GetQueueUpEnd()->UnregisterDequeue(); } } classic::ConnectionCloseEvent event; event.mutable_remote()->set_address(channel_->GetDevice().GetAddress().ToString()); event.set_reason(static_cast<uint32_t>(error_code)); Loading @@ -188,6 +212,20 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service channel_ = nullptr; } void SuspendDequeue() { if (dequeue_registered_.exchange(false)) { channel_->GetQueueUpEnd()->UnregisterDequeue(); } } void ResumeDequeue() { if (!dequeue_registered_.exchange(true)) { channel_->GetQueueUpEnd()->RegisterDequeue( facade_service_->facade_handler_, common::Bind(&L2capDynamicChannelHelper::on_incoming_packet, common::Unretained(this))); } } void on_connect_fail(DynamicChannelManager::ConnectionResult result) {} void on_incoming_packet() { Loading Loading @@ -237,6 +275,7 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service std::unique_ptr<DynamicChannel> channel_ = nullptr; Psm psm_; RetransmissionFlowControlMode mode_ = RetransmissionFlowControlMode::BASIC; std::atomic_bool dequeue_registered_ = false; std::condition_variable channel_open_cv_; std::mutex channel_open_cv_mutex_; }; Loading
system/gd/l2cap/classic/facade.proto +14 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,11 @@ service L2capClassicModuleFacade { rpc FetchL2capData(google.protobuf.Empty) returns (stream L2capPacket) {} rpc SetDynamicChannel(SetEnableDynamicChannelRequest) returns (google.protobuf.Empty) {} rpc SendDynamicChannelPacket(DynamicChannelPacket) returns (google.protobuf.Empty) {} rpc SetTrafficPaused(SetTrafficPausedRequest) returns (google.protobuf.Empty) {} rpc GetChannelQueueDepth(google.protobuf.Empty) returns (GetChannelQueueDepthResponse) { // Get the buffer size of channel queue end for L2CAP user (how many packets we can buffer // before L2CAP user dequeues. } } message RegisterChannelRequest { Loading Loading @@ -87,3 +92,12 @@ message DynamicChannelPacket { uint32 psm = 2; bytes payload = 3; } message SetTrafficPausedRequest { bool paused = 1; uint32 psm = 2; } message GetChannelQueueDepthResponse { uint32 size = 1; }
system/gd/l2cap/internal/dynamic_channel_impl.h +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,7 @@ class DynamicChannelImpl : public l2cap::internal::ChannelImpl { // Internal states bool closed_ = false; hci::ErrorCode close_reason_ = hci::ErrorCode::SUCCESS; static constexpr size_t kChannelQueueSize = 10; static constexpr size_t kChannelQueueSize = 5; common::BidiQueue<packet::PacketView<packet::kLittleEndian>, packet::BasePacketBuilder> channel_queue_{ kChannelQueueSize}; Loading