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

Commit 1e8a1c28 authored by Ted Wang's avatar Ted Wang
Browse files

L2CAP: Implement configuration negotiation to support mandatory/optional

use of ERTM

* To support mandatory use of ERTM, gd L2CAP should initiate
  disconnection when remote does not support/wish or attempts to
  configure to Basic Mode.
* Add ENHANCED_RETRANSMISSION_OPTION in RetransmissionAndFlowControlMode
  to specified mandatory and optional.
* Add FAIL_REMOTE_NOT_SUPPORT to indicate upper layer of connection
  channel fail due to remote no supporting ERTM when using mandatory
  ERTM.
* Add cert test cases:
    test_config_respond_basic_mode_when_using_mandatory_ertm
    test_config_request_basic_mode_when_using_mandatory_ertm
    test_respond_not_support_ertm_when_using_mandatory_ertm

Test: cert/run --host
Bug: 151049713
Change-Id: Id383a113f648acb1061668bb24f49256afb9bd6f
parent 7f922fd6
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)
@@ -77,19 +78,19 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
                                  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(),
@@ -139,7 +140,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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(
@@ -332,7 +333,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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(
@@ -349,7 +350,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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(
@@ -366,7 +367,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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(
@@ -381,7 +382,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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(
@@ -413,7 +414,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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(
@@ -451,7 +452,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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(
@@ -472,7 +473,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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')
@@ -496,7 +497,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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')
@@ -524,7 +525,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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
@@ -542,7 +543,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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(
@@ -558,7 +559,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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')

@@ -578,7 +579,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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))
@@ -596,7 +597,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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')
@@ -620,7 +621,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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')

@@ -642,7 +643,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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')

@@ -665,7 +666,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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')

@@ -691,7 +692,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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))
@@ -720,7 +721,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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')
@@ -750,7 +751,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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')
@@ -778,7 +779,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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')
@@ -807,7 +808,8 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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

@@ -825,7 +827,7 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        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))
@@ -837,3 +839,41 @@ class L2capTest(GdFacadeOnlyBaseTestClass):
        # 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
@@ -224,16 +224,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,
@@ -249,9 +243,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_);
    }

@@ -340,6 +337,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