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

Commit 2dcec586 authored by Hansong Zhang's avatar Hansong Zhang Committed by android-build-merger
Browse files

L2CAP Integrate channel mode in configuration

am: a9b1a8b6

Change-Id: I6255cc7b0234b29be78a86751a64a877e5823285
parents bc46d20b a9b1a8b6
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