Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e0bebca4 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "L2CAP: Implement configuration negotiation to support mandatory/optional use of ERTM"

parents 9d99beeb 1e8a1c28
Loading
Loading
Loading
Loading
+76 −3
Original line number Diff line number Diff line
@@ -119,6 +119,10 @@ class CertL2cap(Closable):

        self.scid_to_dcid = {}

        self.support_ertm = True
        self.support_fcs = True

        self.basic_option = None
        self.ertm_option = None
        self.fcs_option = None

@@ -159,6 +163,14 @@ class CertL2cap(Closable):
    def _get_acl_stream(self):
        return self._acl_manager.get_acl_stream()

    # Disable ERTM when exchange extened feature
    def disable_ertm(self):
        self.support_ertm = False

    # Disable FCS when exchange extened feature
    def disable_fcs(self):
        self.support_fcs = False

    def turn_on_ertm(self, tx_window_size=10, max_transmit=20):
        self.ertm_option = l2cap_packets.RetransmissionAndFlowControlConfigurationOption(
        )
@@ -196,6 +208,25 @@ class CertL2cap(Closable):
            CommandCode.
            CONNECTION_RESPONSE] = self._on_connection_response_configuration_request_with_continuation_flag

    # more of a hack for the moment
    def reply_with_basic_mode(self):
        self.control_table[
            CommandCode.
            CONFIGURATION_REQUEST] = self._on_configuration_request_basic_mode

    # more of a hack for the moment
    def reply_with_nothing(self):
        self.control_table[
            CommandCode.
            CONNECTION_RESPONSE] = self._on_connection_response_do_nothing

    # more of a hack for the moment
    # Send configuration request after receive configuration request
    def config_with_basic_mode(self):
        self.control_table[
            CommandCode.
            CONFIGURATION_REQUEST] = self._on_configuration_request_send_configuration_request_basic_mode

    def _on_connection_request_default(self, l2cap_control_view):
        connection_request_view = l2cap_packets.ConnectionRequestView(
            l2cap_control_view)
@@ -220,7 +251,9 @@ class CertL2cap(Closable):
        self.scid_to_dcid[scid] = dcid

        options = []
        if self.ertm_option is not None:
        if self.basic_option is not None:
            options.append(self.basic_option)
        elif self.ertm_option is not None:
            options.append(self.ertm_option)
        if self.fcs_option is not None:
            options.append(self.fcs_option)
@@ -281,6 +314,14 @@ class CertL2cap(Closable):
        self.get_control_channel().send(config_request)
        return True

    def _on_connection_response_do_nothing(self, l2cap_control_view):
        connection_response_view = l2cap_packets.ConnectionResponseView(
            l2cap_control_view)
        sid = connection_response_view.GetIdentifier()
        scid = connection_response_view.GetSourceCid()
        dcid = connection_response_view.GetDestinationCid()
        self.scid_to_dcid[scid] = dcid

    def _on_configuration_request_default(self, l2cap_control_view):
        configuration_request = l2cap_packets.ConfigurationRequestView(
            l2cap_control_view)
@@ -312,6 +353,38 @@ class CertL2cap(Closable):
            [mtu_opt, fcs_opt, rfc_opt])
        self.control_channel.send(config_response)

    def _on_configuration_request_basic_mode(self, l2cap_control_view):
        configuration_request = l2cap_packets.ConfigurationRequestView(
            l2cap_control_view)
        sid = configuration_request.GetIdentifier()
        dcid = configuration_request.GetDestinationCid()

        basic_option = l2cap_packets.RetransmissionAndFlowControlConfigurationOption(
        )
        basic_option.mode = l2cap_packets.RetransmissionAndFlowControlModeOption.L2CAP_BASIC

        config_response = l2cap_packets.ConfigurationResponseBuilder(
            sid, self.scid_to_dcid.get(dcid, 0), l2cap_packets.Continuation.END,
            l2cap_packets.ConfigurationResponseResult.UNACCEPTABLE_PARAMETERS,
            [basic_option])
        self.control_channel.send(config_response)

    def _on_configuration_request_send_configuration_request_basic_mode(
            self, l2cap_control_view):
        configuration_request = l2cap_packets.ConfigurationRequestView(
            l2cap_control_view)
        sid = configuration_request.GetIdentifier()
        dcid = configuration_request.GetDestinationCid()

        basic_option = l2cap_packets.RetransmissionAndFlowControlConfigurationOption(
        )
        basic_option.mode = l2cap_packets.RetransmissionAndFlowControlModeOption.L2CAP_BASIC

        config_request = l2cap_packets.ConfigurationRequestBuilder(
            sid + 1, self.scid_to_dcid.get(dcid, 0),
            l2cap_packets.Continuation.END, [basic_option])
        self.control_channel.send(config_request)

    def _on_configuration_response_default(self, l2cap_control_view):
        configuration_response = l2cap_packets.ConfigurationResponseView(
            l2cap_control_view)
@@ -343,8 +416,8 @@ class CertL2cap(Closable):
            return
        if information_type == l2cap_packets.InformationRequestInfoType.EXTENDED_FEATURES_SUPPORTED:
            response = l2cap_packets.InformationResponseExtendedFeaturesBuilder(
                sid, l2cap_packets.InformationRequestResult.SUCCESS, 0, 0, 0, 1,
                0, 1, 0, 0, 0, 0)
                sid, l2cap_packets.InformationRequestResult.SUCCESS, 0, 0, 0, self.support_ertm,
                0, self.support_fcs, 0, 0, 0, 0)
            self.control_channel.send(response)
            return
        if information_type == l2cap_packets.InformationRequestInfoType.FIXED_CHANNELS_SUPPORTED:
+70 −30
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ from bluetooth_packets_python3.l2cap_packets import SupervisoryFunction
from bluetooth_packets_python3.l2cap_packets import Poll
from bluetooth_packets_python3.l2cap_packets import InformationRequestInfoType
from l2cap.classic.cert.cert_l2cap import CertL2cap
from l2cap.classic.facade_pb2 import RetransmissionFlowControlMode

# Assemble a sample packet. TODO: Use RawBuilder
SAMPLE_PACKET = l2cap_packets.CommandRejectNotUnderstoodBuilder(1)
@@ -78,19 +79,19 @@ class L2capTest(GdBaseTestClass):
                                  signal_id=1,
                                  scid=0x0101,
                                  psm=0x33,
                                  use_ertm=False):

        mode = l2cap_facade_pb2.RetransmissionFlowControlMode.BASIC
        if use_ertm:
            mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
                                  mode=RetransmissionFlowControlMode.BASIC):

        dut_channel = self.dut_l2cap.open_channel(psm, mode)
        cert_channel = self.cert_l2cap.open_channel(signal_id, psm, scid)

        return (dut_channel, cert_channel)

    def _open_channel(self, signal_id=1, scid=0x0101, psm=0x33, use_ertm=False):
        result = self._open_unvalidated_channel(signal_id, scid, psm, use_ertm)
    def _open_channel(self,
                      signal_id=1,
                      scid=0x0101,
                      psm=0x33,
                      mode=RetransmissionFlowControlMode.BASIC):
        result = self._open_unvalidated_channel(signal_id, scid, psm, mode)

        assertThat(self.cert_l2cap.get_control_channel()).emits(
            L2capMatchers.ConfigurationResponse(),
@@ -129,7 +130,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc' * 34)
        assertThat(cert_channel).emits(
@@ -322,7 +323,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        assertThat(cert_channel).emits(
@@ -339,7 +340,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_fcs()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        assertThat(cert_channel).emits(
@@ -356,7 +357,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_fcs()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        assertThat(cert_channel).emits(
@@ -371,7 +372,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        assertThat(cert_channel).emits(
@@ -403,7 +404,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        for i in range(3):
            cert_channel.send_i_frame(
@@ -441,7 +442,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        for i in range(3):
            cert_channel.send_i_frame(
@@ -462,7 +463,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm(tx_window_size=1)

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        dut_channel.send(b'def')
@@ -486,7 +487,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm(tx_window_size=1)

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        dut_channel.send(b'def')
@@ -514,7 +515,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        # TODO: Always use their retransmission timeout value
@@ -532,7 +533,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        cert_channel.send_s_frame(req_seq=0, p=Poll.POLL)
        assertThat(cert_channel).emits(
@@ -548,7 +549,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')

@@ -568,7 +569,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        assertThat(cert_channel).emits(L2capMatchers.IFrame(tx_seq=0))
@@ -586,7 +587,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm(tx_window_size=2, max_transmit=2)

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        dut_channel.send(b'abc')
@@ -661,7 +662,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')

@@ -683,7 +684,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')

@@ -706,7 +707,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')

@@ -732,7 +733,7 @@ class L2capTest(GdBaseTestClass):
        ertm_tx_window_size = 5

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x41, use_ertm=True)
            scid=0x41, psm=0x41, mode=RetransmissionFlowControlMode.ERTM)

        cert_channel.send_i_frame(tx_seq=0, req_seq=0, payload=SAMPLE_PACKET)
        assertThat(cert_channel).emits(L2capMatchers.SFrame(req_seq=1))
@@ -761,7 +762,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        dut_channel.send(b'abc')
@@ -791,7 +792,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        dut_channel.send(b'abc')
@@ -819,7 +820,7 @@ class L2capTest(GdBaseTestClass):
        self.cert_l2cap.turn_on_ertm()

        (dut_channel, cert_channel) = self._open_channel(
            scid=0x41, psm=0x33, use_ertm=True)
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        dut_channel.send(b'abc')
        dut_channel.send(b'abc')
@@ -848,7 +849,8 @@ class L2capTest(GdBaseTestClass):
        self._setup_link_from_cert()
        self.cert_l2cap.turn_on_ertm()

        self._open_unvalidated_channel(scid=0x41, psm=0x33, use_ertm=True)
        self._open_unvalidated_channel(
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)

        # TODO: Fix this test. It doesn't work so far with PDL struct

@@ -866,7 +868,7 @@ class L2capTest(GdBaseTestClass):
        self._setup_link_from_cert()
        psm = 1
        scid = 0x0101
        self.retransmission_mode = l2cap_facade_pb2.RetransmissionFlowControlMode.ERTM
        self.retransmission_mode = RetransmissionFlowControlMode.ERTM
        self.dut.l2cap.SetDynamicChannel(
            l2cap_facade_pb2.SetEnableDynamicChannelRequest(
                psm=psm, retransmission_mode=self.retransmission_mode))
@@ -878,3 +880,41 @@ class L2capTest(GdBaseTestClass):
        # TODO: Verify that the type should be ERTM
        assertThat(self.cert_l2cap.get_control_channel()).emits(
            L2capMatchers.ConfigurationResponse())

    def test_respond_not_support_ertm_when_using_mandatory_ertm(self):
        """
        L2CAP/CMC/BV-12-C
        """
        self._setup_link_from_cert()

        self.dut.l2cap.OpenChannel(
            l2cap_facade_pb2.OpenChannelRequest(
                remote=self.cert_address, psm=0x33, mode=RetransmissionFlowControlMode.ERTM))
        assertThat(self.cert_l2cap.get_control_channel()).emitsNone(
            L2capMatchers.ConfigurationRequest())

    def test_config_respond_basic_mode_when_using_mandatory_ertm(self):
        """
        L2CAP/CMC/BI-01-C
        """
        self._setup_link_from_cert()
        self.cert_l2cap.reply_with_nothing()
        self.cert_l2cap.reply_with_basic_mode()
        self._open_unvalidated_channel(
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)
        assertThat(self.cert_l2cap.get_control_channel()).emits(
            L2capMatchers.ConfigurationRequest(),
            L2capMatchers.DisconnectionRequest()).inOrder()

    def test_config_request_basic_mode_when_using_mandatory_ertm(self):
        """
        L2CAP/CMC/BI-02-C
        """
        self._setup_link_from_cert()
        self.cert_l2cap.reply_with_nothing()
        self.cert_l2cap.config_with_basic_mode()
        self._open_unvalidated_channel(
            scid=0x41, psm=0x33, mode=RetransmissionFlowControlMode.ERTM)
        assertThat(self.cert_l2cap.get_control_channel()).emits(
            L2capMatchers.ConfigurationRequest(),
            L2capMatchers.DisconnectionRequest()).inOrder()
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ struct DynamicChannelConfigurationOption {
  enum class RetransmissionAndFlowControlMode {
    L2CAP_BASIC,
    ENHANCED_RETRANSMISSION,
    ENHANCED_RETRANSMISSION_OPTIONAL,
  };
  /**
   * Retransmission and flow control mode. Currently L2CAP_BASIC and ENHANCED_RETRANSMISSION.
+1 −0
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ class DynamicChannelManager {
    FAIL_NO_SERVICE_REGISTERED = 1,  // No service is registered
    FAIL_HCI_ERROR = 2,              // See hci_error
    FAIL_L2CAP_ERROR = 3,            // See l2cap_connection_response_result
    FAIL_REMOTE_NOT_SUPPORT = 4,     // Remote not support required retansmission and flow control mode
  };

  struct ConnectionResult {
+8 −10
Original line number Diff line number Diff line
@@ -113,16 +113,10 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
   public:
    L2capDynamicChannelHelper(L2capClassicModuleFacadeService* service, L2capClassicModule* l2cap_layer,
                              os::Handler* handler, Psm psm, RetransmissionFlowControlMode mode)
        : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), psm_(psm) {
        : facade_service_(service), l2cap_layer_(l2cap_layer), handler_(handler), psm_(psm), mode_(mode) {
      dynamic_channel_manager_ = l2cap_layer_->GetDynamicChannelManager();
      DynamicChannelConfigurationOption configuration_option = {};
      if (mode == RetransmissionFlowControlMode::BASIC) {
        configuration_option.channel_mode =
            DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::L2CAP_BASIC;
      } else if (mode == RetransmissionFlowControlMode::ERTM) {
        configuration_option.channel_mode =
            DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode::ENHANCED_RETRANSMISSION;
      }
      configuration_option.channel_mode = (DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode)mode;
      dynamic_channel_manager_->RegisterService(
          psm, configuration_option, {},
          common::BindOnce(&L2capDynamicChannelHelper::on_l2cap_service_registration_complete,
@@ -138,9 +132,12 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
    }

    void Connect(hci::Address address) {
      // TODO: specify channel mode
      DynamicChannelConfigurationOption configuration_option = l2cap::classic::DynamicChannelConfigurationOption();
      configuration_option.channel_mode = (DynamicChannelConfigurationOption::RetransmissionAndFlowControlMode)mode_;

      dynamic_channel_manager_->ConnectChannel(
          address, {}, psm_, common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)),
          address, configuration_option, psm_,
          common::Bind(&L2capDynamicChannelHelper::on_connection_open, common::Unretained(this)),
          common::Bind(&L2capDynamicChannelHelper::on_connect_fail, common::Unretained(this)), handler_);
    }

@@ -229,6 +226,7 @@ class L2capClassicModuleFacadeService : public L2capClassicModuleFacade::Service
    std::unique_ptr<DynamicChannelService> service_;
    std::unique_ptr<DynamicChannel> channel_ = nullptr;
    Psm psm_;
    RetransmissionFlowControlMode mode_ = RetransmissionFlowControlMode::BASIC;
    std::condition_variable channel_open_cv_;
    std::mutex channel_open_cv_mutex_;
  };
Loading