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

Commit a9b1a8b6 authored by Hansong Zhang's avatar Hansong Zhang
Browse files

L2CAP Integrate channel mode in configuration

Store the channel retransmission and flow control mode requirement from
user.

Enable ERTM mode when specified in channel registration and connection
request from client.

Add the first POC for sending I-Frame.

FCS is not supported yet.

Bug: 144770885
Test: run_cert.sh and bluetooth_test_gd
Change-Id: I4a6f01ad31573e18546134007a1e95cb5f2d215b
parent cf7424e8
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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;
}

+27 −2
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ service L2capModuleCert {
  rpc SendInformationResponse(InformationResponse) returns (SendInformationResponseResult) {}

  rpc FetchL2capLog(FetchL2capLogRequest) returns (stream FetchL2capLogResponse) {}
  rpc StopFetchingL2capLog(StopFetchingL2capLogRequest) returns (StopFetchingL2capLogResponse) {}
}

message L2capPacket {
@@ -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 {}
@@ -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 {}
@@ -107,6 +127,7 @@ message InformationResponse {
  InformationRequestType type = 1;
  uint32 data = 2;
  uint32 signal_id = 3;
  uint32 information_value = 4;
}

message SendInformationResponseResult {}
@@ -153,3 +174,7 @@ message FetchL2capLogResponse {
    LinkDown link_down = 21;
  }
}

message StopFetchingL2capLogRequest {}

message StopFetchingL2capLogResponse {}
+56 −3
Original line number Diff line number Diff line
@@ -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();
@@ -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();
@@ -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);
@@ -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_;
@@ -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;
          }
        }
+85 −9
Original line number Diff line number Diff line
@@ -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]
@@ -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):
@@ -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):
@@ -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 = []
@@ -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)
@@ -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):
        """
@@ -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())
@@ -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)
@@ -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)
@@ -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)
@@ -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
+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