text: // Copyright 2018 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "src/connectivity/bluetooth/core/bt-host/l2cap/bredr_dynamic_channel.h"
  
  #include <lib/async/cpp/task.h>
  
  #include <vector>
  
  #include <gtest/gtest.h>
  
  #include "lib/gtest/test_loop_fixture.h"
  #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
  #include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
  #include "src/connectivity/bluetooth/core/bt-host/hci/hci.h"
  #include "src/connectivity/bluetooth/core/bt-host/l2cap/fake_signaling_channel.h"
  #include "src/connectivity/bluetooth/core/bt-host/l2cap/l2cap_defs.h"
  
  namespace bt::l2cap::internal {
  namespace {
  
  // TODO(fxbug.dev/1049): Add integration test with FakeChannelTest and
  // BrEdrSignalingChannel using snooped connection data to verify signaling
  // channel traffic.
  
  constexpr uint16_t kPsm = 0x0001;
  constexpr uint16_t kInvalidPsm = 0x0002;  // Valid PSMs are odd.
  constexpr ChannelId kLocalCId = 0x0040;
  constexpr ChannelId kLocalCId2 = 0x0041;
  constexpr ChannelId kRemoteCId = 0x60a3;
  constexpr ChannelId kBadCId = 0x003f;  // Not a dynamic channel.
  
  constexpr ChannelParameters kChannelParams;
  constexpr ChannelParameters kERTMChannelParams{ChannelMode::kEnhancedRetransmission, std::nullopt};
  
  // Commands Reject
  
  const ByteBuffer& kRejNotUnderstood = CreateStaticByteBuffer(
      // Reject Reason (Not Understood)
      0x00, 0x00);
  
  // Connection Requests
  
  const ByteBuffer& kConnReq = CreateStaticByteBuffer(
      // PSM
      LowerBits(kPsm), UpperBits(kPsm),
  
      // Source CID
      LowerBits(kLocalCId), UpperBits(kLocalCId));
  
  auto MakeConnectionRequest(ChannelId src_id, PSM psm) {
    return CreateStaticByteBuffer(
        // PSM
        LowerBits(psm), UpperBits(psm),
  
        // Source CID
        LowerBits(src_id), UpperBits(src_id));
  }
  
  const ByteBuffer& kInboundConnReq = CreateStaticByteBuffer(
      // PSM
      LowerBits(kPsm), UpperBits(kPsm),
  
      // Source CID
      LowerBits(kRemoteCId), UpperBits(kRemoteCId));
  
  const ByteBuffer& kInboundInvalidPsmConnReq = CreateStaticByteBuffer(
      // PSM
      LowerBits(kInvalidPsm), UpperBits(kInvalidPsm),
  
      // Source CID
      LowerBits(kRemoteCId), UpperBits(kRemoteCId));
  
  const ByteBuffer& kInboundBadCIdConnReq = CreateStaticByteBuffer(
      // PSM
      LowerBits(kPsm), UpperBits(kPsm),
  
      // Source CID
      LowerBits(kBadCId), UpperBits(kBadCId));
  
  // Connection Responses
  
  const ByteBuffer& kPendingConnRsp = CreateStaticByteBuffer(
      // Destination CID
      0x00, 0x00,
  
      // Source CID
      LowerBits(kLocalCId), UpperBits(kLocalCId),
  
      // Result (Pending)
      0x01, 0x00,
  
      // Status (Authorization Pending)
      0x02, 0x00);
  
  const ByteBuffer& kPendingConnRspWithId = CreateStaticByteBuffer(
      // Destination CID (Wrong endianness but valid)
      UpperBits(kRemoteCId), LowerBits(kRemoteCId),
  
      // Source CID
      LowerBits(kLocalCId), UpperBits(kLocalCId),
  
      // Result (Pending)
      0x01, 0x00,
  
      // Status (Authorization Pending)
      0x02, 0x00);
  
  auto MakeConnectionResponseWithResultPending(ChannelId src_id, ChannelId dst_id) {
    return CreateStaticByteBuffer(
        // Destination CID
        LowerBits(dst_id), UpperBits(dst_id),
  
        // Source CID
        LowerBits(src_id), UpperBits(src_id),
  
        // Result (Pending)
        0x01, 0x00,
  
        // Status (Authorization Pending)
        0x02, 0x00);
  }
  
  const ByteBuffer& kOkConnRsp = CreateStaticByteBuffer(
      // Destination CID
      LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
      // Source CID
      LowerBits(kLocalCId), UpperBits(kLocalCId),
  
      // Result (Successful)
      0x00, 0x00,
  
      // Status (No further information available)
      0x00, 0x00);
  
  auto MakeConnectionResponse(ChannelId src_id, ChannelId dst_id) {
    return CreateStaticByteBuffer(
        // Destination CID
        LowerBits(dst_id), UpperBits(dst_id),
  
        // Source CID
        LowerBits(src_id), UpperBits(src_id),
  
        // Result (Successful)
        0x00, 0x00,
  
        // Status (No further information available)
        0x00, 0x00);
  }
  
  const ByteBuffer& kInvalidConnRsp = CreateStaticByteBuffer(
      // Destination CID (Not a dynamic channel ID)
      LowerBits(kBadCId), UpperBits(kBadCId),
  
      // Source CID
      LowerBits(kLocalCId), UpperBits(kLocalCId),
  
      // Result (Successful)
      0x00, 0x00,
  
      // Status (No further information available)
      0x00, 0x00);
  
  const ByteBuffer& kRejectConnRsp = CreateStaticByteBuffer(
      // Destination CID (Invalid)
      LowerBits(kInvalidChannelId), UpperBits(kInvalidChannelId),
  
      // Source CID
      LowerBits(kLocalCId), UpperBits(kLocalCId),
  
      // Result (No resources)
      0x04, 0x00,
  
      // Status (No further information available)
      0x00, 0x00);
  
  const ByteBuffer& kInboundOkConnRsp = CreateStaticByteBuffer(
      // Destination CID
      LowerBits(kLocalCId), UpperBits(kLocalCId),
  
      // Source CID
      LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
      // Result (Successful)
      0x00, 0x00,
  
      // Status (No further information available)
      0x00, 0x00);
  
  const ByteBuffer& kOutboundSourceCIdAlreadyAllocatedConnRsp = CreateStaticByteBuffer(
      // Destination CID (Invalid)
      0x00, 0x00,
  
      // Source CID (Invalid)
      LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
      // Result (Connection refused - source CID already allocated)
      0x07, 0x00,
  
      // Status (No further information available)
      0x00, 0x00);
  
  const ByteBuffer& kInboundBadPsmConnRsp = CreateStaticByteBuffer(
      // Destination CID (Invalid)
      0x00, 0x00,
  
      // Source CID
      LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
      // Result (PSM Not Supported)
      0x02, 0x00,
  
      // Status (No further information available)
      0x00, 0x00);
  
  const ByteBuffer& kInboundBadCIdConnRsp = CreateStaticByteBuffer(
      // Destination CID (Invalid)
      0x00, 0x00,
  
      // Source CID
      LowerBits(kBadCId), UpperBits(kBadCId),
  
      // Result (Invalid Source CID)
      0x06, 0x00,
  
      // Status (No further information available)
      0x00, 0x00);
  
  // Disconnection Requests
  
  const ByteBuffer& kDisconReq = CreateStaticByteBuffer(
      // Destination CID
      LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
      // Source CID
      LowerBits(kLocalCId), UpperBits(kLocalCId));
  
  const ByteBuffer& kInboundDisconReq = CreateStaticByteBuffer(
      // Destination CID
      LowerBits(kLocalCId), UpperBits(kLocalCId),
  
      // Source CID
      LowerBits(kRemoteCId), UpperBits(kRemoteCId));
  
  // Disconnection Responses
  
  const ByteBuffer& kInboundDisconRsp = kInboundDisconReq;
  
  const ByteBuffer& kDisconRsp = kDisconReq;
  
  // Configuration Requests
  
  auto MakeConfigReqWithMtuAndRfc(ChannelId dest_cid, uint16_t mtu, ChannelMode mode,
                                  uint8_t tx_window, uint8_t max_transmit,
                                  uint16_t retransmission_timeout, uint16_t monitor_timeout,
                                  uint16_t mps) {
    return StaticByteBuffer(
        // Destination CID
        LowerBits(dest_cid), UpperBits(dest_cid),
  
        // Flags
        0x00, 0x00,
  
        // MTU option (Type, Length, MTU value)
        0x01, 0x02, LowerBits(mtu), UpperBits(mtu),
  
        // Retransmission & Flow Control option (Type, Length = 9, mode, unused fields)
        0x04, 0x09, static_cast<uint8_t>(mode), tx_window, max_transmit,
        LowerBits(retransmission_timeout), UpperBits(retransmission_timeout),
        LowerBits(monitor_timeout), UpperBits(monitor_timeout), LowerBits(mps), UpperBits(mps));
  }
  
  auto MakeConfigReqWithMtu(ChannelId dest_cid, uint16_t mtu = kMaxMTU, uint16_t flags = 0x0000) {
    return StaticByteBuffer(
        // Destination CID
        LowerBits(dest_cid), UpperBits(dest_cid),
  
        // Flags
        LowerBits(flags), UpperBits(flags),
  
        // MTU option (Type, Length, MTU value)
        0x01, 0x02, LowerBits(mtu), UpperBits(mtu));
  }
  
  const ByteBuffer& kOutboundConfigReq = MakeConfigReqWithMtu(kRemoteCId);
  
  const ByteBuffer& kOutboundConfigReqWithErtm = MakeConfigReqWithMtuAndRfc(
      kRemoteCId, kMaxInboundPduPayloadSize, ChannelMode::kEnhancedRetransmission,
      kErtmMaxUnackedInboundFrames, kErtmMaxInboundRetransmissions, 0, 0, kMaxInboundPduPayloadSize);
  
  const ByteBuffer& kInboundConfigReq = CreateStaticByteBuffer(
      // Destination CID
      LowerBits(kLocalCId), UpperBits(kLocalCId),
  
      // Flags
      0x00, 0x00);
  
  const ByteBuffer& kInboundConfigReq2 = CreateStaticByteBuffer(
      // Destination CID
      LowerBits(kLocalCId2), UpperBits(kLocalCId2),
  
      // Flags
      0x00, 0x00);
  
  // Use plausible ERTM parameters that do not necessarily match values in production. See Core Spec
  // v5.0 Vol 3, Part A, Sec 5.4 for meanings.
  constexpr uint8_t kErtmNFramesInTxWindow = 32;
  constexpr uint8_t kErtmMaxTransmissions = 8;
  constexpr uint16_t kMaxTxPduPayloadSize = 1024;
  
  const ByteBuffer& kInboundConfigReqWithERTM = CreateStaticByteBuffer(
      // Destination CID
      LowerBits(kLocalCId), UpperBits(kLocalCId),
  
      // Flags
      0x00, 0x00,
  
      // Retransmission & Flow Control option (Type, Length = 9, mode = ERTM, dummy parameters)
      0x04, 0x09, 0x03, kErtmNFramesInTxWindow, kErtmMaxTransmissions, 0x00, 0x00, 0x00, 0x00,
      LowerBits(kMaxTxPduPayloadSize), UpperBits(kMaxTxPduPayloadSize));
  
  // Configuration Responses
  
  auto MakeEmptyConfigRsp(ChannelId src_id,
                          ConfigurationResult result = ConfigurationResult::kSuccess,
                          uint16_t flags = 0x0000) {
    return CreateStaticByteBuffer(
        // Source CID
        LowerBits(src_id), UpperBits(src_id),
  
        // Flags
        LowerBits(flags), UpperBits(flags),
  
        // Result
        LowerBits(static_cast<uint16_t>(result)), UpperBits(static_cast<uint16_t>(result)));
  }
  
  const ByteBuffer& kOutboundEmptyContinuationConfigRsp =
      MakeEmptyConfigRsp(kRemoteCId, ConfigurationResult::kSuccess, kConfigurationContinuation);
  
  const ByteBuffer& kInboundEmptyConfigRsp = MakeEmptyConfigRsp(kLocalCId);
  
  const ByteBuffer& kUnknownIdConfigRsp = MakeEmptyConfigRsp(kBadCId);
  
  const ByteBuffer& kOutboundEmptyPendingConfigRsp =
      MakeEmptyConfigRsp(kRemoteCId, ConfigurationResult::kPending);
  
  const ByteBuffer& kInboundEmptyPendingConfigRsp =
      MakeEmptyConfigRsp(kLocalCId, ConfigurationResult::kPending);
  
  auto MakeConfigRspWithMtu(ChannelId source_cid, uint16_t mtu,
                            ConfigurationResult result = ConfigurationResult::kSuccess,
                            uint16_t flags = 0x0000) {
    return CreateStaticByteBuffer(
        // Source CID
        LowerBits(source_cid), UpperBits(source_cid),
  
        // Flags
        LowerBits(flags), UpperBits(flags),
  
        // Result
        LowerBits(static_cast<uint16_t>(result)), UpperBits(static_cast<uint16_t>(result)),
  
        // MTU option (Type, Length, MTU value)
        0x01, 0x02, LowerBits(mtu), UpperBits(mtu));
  }
  
  const ByteBuffer& kOutboundOkConfigRsp = MakeConfigRspWithMtu(kRemoteCId, kDefaultMTU);
  
  auto MakeConfigRspWithRfc(ChannelId source_cid, ConfigurationResult result, ChannelMode mode,
                            uint8_t tx_window, uint8_t max_transmit, uint16_t retransmission_timeout,
                            uint16_t monitor_timeout, uint16_t mps) {
    return CreateStaticByteBuffer(
        // Source CID
        LowerBits(source_cid), UpperBits(source_cid),
  
        // Flags
        0x00, 0x00,
  
        // Result
        LowerBits(static_cast<uint16_t>(result)), UpperBits(static_cast<uint16_t>(result)),
  
        // Retransmission & Flow Control option (Type, Length: 9, mode, unused parameters)
        0x04, 0x09, static_cast<uint8_t>(mode), tx_window, max_transmit,
        LowerBits(retransmission_timeout), UpperBits(retransmission_timeout),
        LowerBits(monitor_timeout), UpperBits(monitor_timeout), LowerBits(mps), UpperBits(mps));
  }
  
  const ByteBuffer& kInboundUnacceptableParamsWithRfcBasicConfigRsp = MakeConfigRspWithRfc(
      kLocalCId, ConfigurationResult::kUnacceptableParameters, ChannelMode::kBasic, 0, 0, 0, 0, 0);
  
  const ByteBuffer& kOutboundUnacceptableParamsWithRfcBasicConfigRsp = MakeConfigRspWithRfc(
      kRemoteCId, ConfigurationResult::kUnacceptableParameters, ChannelMode::kBasic, 0, 0, 0, 0, 0);
  
  const ByteBuffer& kOutboundUnacceptableParamsWithRfcERTMConfigRsp =
      MakeConfigRspWithRfc(kRemoteCId, ConfigurationResult::kUnacceptableParameters,
                           ChannelMode::kEnhancedRetransmission, 0, 0, 0, 0, 0);
  
  auto MakeConfigRspWithMtuAndRfc(ChannelId source_cid, ConfigurationResult result, ChannelMode mode,
                                  uint16_t mtu, uint8_t tx_window, uint8_t max_transmit,
                                  uint16_t retransmission_timeout, uint16_t monitor_timeout,
                                  uint16_t mps) {
    return StaticByteBuffer(
        // Source CID
        LowerBits(source_cid), UpperBits(source_cid),
  
        // Flags
        0x00, 0x00,
  
        // Result
        LowerBits(static_cast<uint16_t>(result)), UpperBits(static_cast<uint16_t>(result)),
  
        // MTU option (Type, Length, MTU value)
        0x01, 0x02, LowerBits(mtu), UpperBits(mtu),
  
        // Retransmission & Flow Control option (Type, Length = 9, mode, ERTM fields)
        0x04, 0x09, static_cast<uint8_t>(mode), tx_window, max_transmit,
        LowerBits(retransmission_timeout), UpperBits(retransmission_timeout),
        LowerBits(monitor_timeout), UpperBits(monitor_timeout), LowerBits(mps), UpperBits(mps));
  }
  
  // Corresponds to kInboundConfigReqWithERTM
  const ByteBuffer& kOutboundOkConfigRspWithErtm = MakeConfigRspWithMtuAndRfc(
      kRemoteCId, ConfigurationResult::kSuccess, ChannelMode::kEnhancedRetransmission, kDefaultMTU,
      kErtmNFramesInTxWindow, kErtmMaxTransmissions, 2000, 12000, kMaxTxPduPayloadSize);
  
  // Information Requests
  
  auto MakeInfoReq(InformationType info_type) {
    const auto type = static_cast<uint16_t>(info_type);
    return CreateStaticByteBuffer(LowerBits(type), UpperBits(type));
  }
  
  const ByteBuffer& kExtendedFeaturesInfoReq =
      MakeInfoReq(InformationType::kExtendedFeaturesSupported);
  
  // Information Responses
  
  auto MakeExtendedFeaturesInfoRsp(InformationResult result = InformationResult::kSuccess,
                                   ExtendedFeatures features = 0u) {
    const auto type = static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported);
    const auto res = static_cast<uint16_t>(result);
    const auto features_bytes = ToBytes(features);
    return CreateStaticByteBuffer(
        // Type
        LowerBits(type), UpperBits(type),
  
        // Result
        LowerBits(res), UpperBits(res),
  
        // Data
        features_bytes[0], features_bytes[1], features_bytes[2], features_bytes[3]);
  }
  
  const ByteBuffer& kExtendedFeaturesInfoRsp =
      MakeExtendedFeaturesInfoRsp(InformationResult::kSuccess);
  const ByteBuffer& kExtendedFeaturesInfoRspWithERTM = MakeExtendedFeaturesInfoRsp(
      InformationResult::kSuccess, kExtendedFeaturesBitEnhancedRetransmission);
  
  class L2CAP_BrEdrDynamicChannelTest : public ::gtest::TestLoopFixture {
   public:
    L2CAP_BrEdrDynamicChannelTest() = default;
    ~L2CAP_BrEdrDynamicChannelTest() override = default;
  
   protected:
    // Import types for brevity.
    using DynamicChannelCallback = DynamicChannelRegistry::DynamicChannelCallback;
    using ServiceRequestCallback = DynamicChannelRegistry::ServiceRequestCallback;
  
    // TestLoopFixture overrides
    void SetUp() override {
      TestLoopFixture::SetUp();
      channel_close_cb_ = nullptr;
      service_request_cb_ = nullptr;
      signaling_channel_ = std::make_unique<testing::FakeSignalingChannel>(dispatcher());
  
      ext_info_transaction_id_ =
          EXPECT_OUTBOUND_REQ(*sig(), kInformationRequest, kExtendedFeaturesInfoReq.view());
      // TODO(63074): Make these tests not rely on strict ordering of channel IDs.
      registry_ = std::make_unique<BrEdrDynamicChannelRegistry>(
          sig(), fit::bind_member(this, &L2CAP_BrEdrDynamicChannelTest::OnChannelClose),
          fit::bind_member(this, &L2CAP_BrEdrDynamicChannelTest::OnServiceRequest),
          /*random_channel_ids=*/false);
    }
  
    void TearDown() override {
      RunLoopUntilIdle();
      registry_ = nullptr;
      signaling_channel_ = nullptr;
      service_request_cb_ = nullptr;
      channel_close_cb_ = nullptr;
      TestLoopFixture::TearDown();
    }
  
    testing::FakeSignalingChannel* sig() const { return signaling_channel_.get(); }
  
    BrEdrDynamicChannelRegistry* registry() const { return registry_.get(); }
  
    void set_channel_close_cb(DynamicChannelCallback close_cb) {
      channel_close_cb_ = std::move(close_cb);
    }
  
    void set_service_request_cb(ServiceRequestCallback service_request_cb) {
      service_request_cb_ = std::move(service_request_cb);
    }
  
    testing::FakeSignalingChannel::TransactionId ext_info_transaction_id() {
      return ext_info_transaction_id_;
    }
  
   private:
    void OnChannelClose(const DynamicChannel* channel) {
      if (channel_close_cb_) {
        channel_close_cb_(channel);
      }
    }
  
    // Default to rejecting all service requests if no test callback is set.
    std::optional<DynamicChannelRegistry::ServiceInfo> OnServiceRequest(PSM psm) {
      if (service_request_cb_) {
        return service_request_cb_(psm);
      }
      return std::nullopt;
    }
  
    DynamicChannelCallback channel_close_cb_;
    ServiceRequestCallback service_request_cb_;
    std::unique_ptr<testing::FakeSignalingChannel> signaling_channel_;
    std::unique_ptr<BrEdrDynamicChannelRegistry> registry_;
    testing::FakeSignalingChannel::TransactionId ext_info_transaction_id_;
  
    DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(L2CAP_BrEdrDynamicChannelTest);
  };
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         InboundConnectionResponseReusingChannelIdCausesInboundChannelFailure) {
    // make successful connection
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_TRUE(chan->IsOpen());
        EXPECT_TRUE(chan->IsConnected());
        EXPECT_EQ(kLocalCId, chan->local_cid());
        EXPECT_EQ(kRemoteCId, chan->remote_cid());
      }
      open_cb_count++;
    };
  
    int close_cb_count = 0;
    set_channel_close_cb([&close_cb_count](auto chan) {
      EXPECT_TRUE(chan);
      close_cb_count++;
    });
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1, open_cb_count);
    EXPECT_EQ(0, close_cb_count);
  
    // simulate inbound request to make new connection using already allocated remote CId
    sig()->ReceiveExpect(kConnectionRequest, kInboundConnReq,
                         kOutboundSourceCIdAlreadyAllocatedConnRsp);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         PeerConnectionResponseReusingChannelIdCausesOutboundChannelFailure) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
  
    // make successful connection
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_TRUE(chan->IsOpen());
        EXPECT_TRUE(chan->IsConnected());
        EXPECT_EQ(kLocalCId, chan->local_cid());
        EXPECT_EQ(kRemoteCId, chan->remote_cid());
      }
      open_cb_count++;
    };
  
    int close_cb_count = 0;
    set_channel_close_cb([&close_cb_count](auto chan) {
      EXPECT_TRUE(chan);
      close_cb_count++;
    });
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1, open_cb_count);
    EXPECT_EQ(0, close_cb_count);
  
    // peer responds with already allocated remote CID
    const auto kConnReq2 = MakeConnectionRequest(kLocalCId2, kPsm);
    const auto kOkConnRspSamePeerCId = MakeConnectionResponse(kLocalCId2, kRemoteCId);
  
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq2.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRspSamePeerCId.view()});
  
    auto channel =
        BrEdrDynamicChannel::MakeOutbound(registry(), sig(), kPsm, kLocalCId2, kChannelParams, false);
    EXPECT_FALSE(channel->IsConnected());
    EXPECT_FALSE(channel->IsOpen());
  
    int close_cb_count2 = 0;
    set_channel_close_cb([&close_cb_count2](auto) { close_cb_count2++; });
  
    int open_cb_count2 = 0;
    channel->Open([&open_cb_count2] { open_cb_count2++; });
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_FALSE(channel->IsConnected());
    EXPECT_FALSE(channel->IsOpen());
    EXPECT_EQ(open_cb_count2, 1);
    EXPECT_EQ(close_cb_count2, 0);
  
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         PeerPendingConnectionResponseReusingChannelIdCausesOutboundChannelFailure) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
  
    // make successful connection
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_TRUE(chan->IsOpen());
        EXPECT_TRUE(chan->IsConnected());
        EXPECT_EQ(kLocalCId, chan->local_cid());
        EXPECT_EQ(kRemoteCId, chan->remote_cid());
      }
      open_cb_count++;
    };
  
    int close_cb_count = 0;
    set_channel_close_cb([&close_cb_count](auto chan) {
      EXPECT_TRUE(chan);
      close_cb_count++;
    });
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1, open_cb_count);
    EXPECT_EQ(0, close_cb_count);
  
    // peer responds with already allocated remote CID
    const auto kConnReq2 = MakeConnectionRequest(kLocalCId2, kPsm);
    const auto kOkConnRspWithResultPendingSamePeerCId =
        MakeConnectionResponseWithResultPending(kLocalCId2, kRemoteCId);
    EXPECT_OUTBOUND_REQ(
        *sig(), kConnectionRequest, kConnReq2.view(),
        {SignalingChannel::Status::kSuccess, kOkConnRspWithResultPendingSamePeerCId.view()});
  
    int open_cb_count2 = 0;
    int close_cb_count2 = 0;
    set_channel_close_cb([&close_cb_count2](auto) { close_cb_count2++; });
  
    registry()->OpenOutbound(kPsm, kChannelParams, [&open_cb_count2](auto) { open_cb_count2++; });
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_EQ(open_cb_count2, 1);
    // A failed-to-open channel should not invoke the close callback.
    EXPECT_EQ(close_cb_count2, 0);
  
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         PeerConnectionResponseWithSameRemoteChannelIdAsPeerPendingConnectionResponseSucceeds) {
    const auto kOkPendingConnRsp = MakeConnectionResponseWithResultPending(kLocalCId, kRemoteCId);
    auto conn_rsp_id =
        EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                            {SignalingChannel::Status::kSuccess, kOkPendingConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_TRUE(chan->IsOpen());
        EXPECT_TRUE(chan->IsConnected());
        EXPECT_EQ(kLocalCId, chan->local_cid());
        EXPECT_EQ(kRemoteCId, chan->remote_cid());
      }
      open_cb_count++;
    };
  
    int close_cb_count = 0;
    set_channel_close_cb([&close_cb_count](auto) { close_cb_count++; });
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(sig()->ReceiveResponses(
        conn_rsp_id, {{SignalingChannel::Status::kSuccess, kOkConnRsp.view()}}));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_EQ(open_cb_count, 1);
    EXPECT_EQ(close_cb_count, 0);
  
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, ChannelDeletedBeforeConnectionResponse) {
    auto conn_id = EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view());
  
    // Build channel and operate it directly to be able to delete it.
    auto channel =
        BrEdrDynamicChannel::MakeOutbound(registry(), sig(), kPsm, kLocalCId, kChannelParams, false);
    ASSERT_TRUE(channel);
  
    int open_result_cb_count = 0;
    channel->Open([&open_result_cb_count] { open_result_cb_count++; });
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    channel = nullptr;
    RETURN_IF_FATAL(sig()->ReceiveResponses(
        conn_id, {{SignalingChannel::Status::kSuccess, kOutboundEmptyPendingConfigRsp.view()}}));
  
    EXPECT_EQ(0, open_result_cb_count);
  
    // No disconnection transaction expected because the channel isn't actually owned by the registry.
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, FailConnectChannel) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kRejectConnRsp.view()});
  
    // Build channel and operate it directly to be able to inspect it in the
    // connected but not open state.
    auto channel =
        BrEdrDynamicChannel::MakeOutbound(registry(), sig(), kPsm, kLocalCId, kChannelParams, false);
    EXPECT_FALSE(channel->IsConnected());
    EXPECT_FALSE(channel->IsOpen());
    EXPECT_EQ(kLocalCId, channel->local_cid());
  
    int open_result_cb_count = 0;
    auto open_result_cb = [&open_result_cb_count, &channel] {
      if (open_result_cb_count == 0) {
        EXPECT_FALSE(channel->IsConnected());
        EXPECT_FALSE(channel->IsOpen());
      }
      open_result_cb_count++;
    };
    int close_cb_count = 0;
    set_channel_close_cb([&close_cb_count](auto) { close_cb_count++; });
  
    channel->Open(std::move(open_result_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_EQ(1, open_result_cb_count);
    EXPECT_FALSE(channel->IsConnected());
    EXPECT_FALSE(channel->IsOpen());
    EXPECT_EQ(kInvalidChannelId, channel->remote_cid());
  
    // A failed-to-open channel should not invoke the close callback.
    EXPECT_EQ(0, close_cb_count);
  
    // No disconnection transaction expected because the channel isn't actually
    // owned by the registry.
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, ConnectChannelFailConfig) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kReject, kRejNotUnderstood.view()});
  
    // Build channel and operate it directly to be able to inspect it in the
    // connected but not open state.
    auto channel =
        BrEdrDynamicChannel::MakeOutbound(registry(), sig(), kPsm, kLocalCId, kChannelParams, false);
    EXPECT_FALSE(channel->IsConnected());
    EXPECT_FALSE(channel->IsOpen());
    EXPECT_EQ(kLocalCId, channel->local_cid());
  
    int open_result_cb_count = 0;
    auto open_result_cb = [&open_result_cb_count, &channel] {
      if (open_result_cb_count == 0) {
        EXPECT_TRUE(channel->IsConnected());
        EXPECT_FALSE(channel->IsOpen());
      }
      open_result_cb_count++;
    };
    int close_cb_count = 0;
    set_channel_close_cb([&close_cb_count](auto) { close_cb_count++; });
  
    channel->Open(std::move(open_result_cb));
    RETURN_IF_FATAL(RunLoopUntilIdle());
    EXPECT_TRUE(channel->IsConnected());
  
    // A connected channel should have a valid remote channel ID.
    EXPECT_EQ(kRemoteCId, channel->remote_cid());
  
    EXPECT_FALSE(channel->IsOpen());
    EXPECT_EQ(1, open_result_cb_count);
  
    // A failed-to-open channel should not invoke the close callback.
    EXPECT_EQ(0, close_cb_count);
  
    // No disconnection transaction expected because the channel isn't actually
    // owned by the registry.
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, ConnectChannelFailInvalidResponse) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kInvalidConnRsp.view()});
  
    // Build channel and operate it directly to be able to inspect it in the
    // connected but not open state.
    auto channel =
        BrEdrDynamicChannel::MakeOutbound(registry(), sig(), kPsm, kLocalCId, kChannelParams, false);
  
    int open_result_cb_count = 0;
    auto open_result_cb = [&open_result_cb_count, &channel] {
      if (open_result_cb_count == 0) {
        EXPECT_FALSE(channel->IsConnected());
        EXPECT_FALSE(channel->IsOpen());
      }
      open_result_cb_count++;
    };
    int close_cb_count = 0;
    set_channel_close_cb([&close_cb_count](auto) { close_cb_count++; });
  
    channel->Open(std::move(open_result_cb));
    RETURN_IF_FATAL(RunLoopUntilIdle());
    EXPECT_FALSE(channel->IsConnected());
    EXPECT_FALSE(channel->IsOpen());
    EXPECT_EQ(1, open_result_cb_count);
    EXPECT_EQ(0, close_cb_count);
  
    // No disconnection transaction expected because the channel isn't actually
    // owned by the registry.
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, OutboundFailsAfterRtxExpiryForConnectionResponse) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kTimeOut, BufferView()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) {
      if (open_cb_count == 0) {
        EXPECT_FALSE(chan);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    // FakeSignalingChannel doesn't need to be clocked in order to simulate a timeout.
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, OutboundFailsAfterErtxExpiryForConnectionResponse) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kPendingConnRsp.view()},
                        {SignalingChannel::Status::kTimeOut, BufferView()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) {
      if (open_cb_count == 0) {
        EXPECT_FALSE(chan);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    // FakeSignalingChannel doesn't need to be clocked in order to simulate a timeout.
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_EQ(1, open_cb_count);
  }
  
  // In L2CAP Test Spec v5.0.2, this is L2CAP/COS/CED/BV-08-C [Disconnect on Timeout].
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         OutboundFailsAndDisconnectsAfterRtxExpiryForConfigurationResponse) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kTimeOut, BufferView()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kTimeOut, BufferView()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) {
      if (open_cb_count == 0) {
        EXPECT_FALSE(chan);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    // FakeSignalingChannel doesn't need to be clocked in order to simulate a timeout.
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_EQ(1, open_cb_count);
  }
  
  // Simulate a ERTX timer expiry after a Configuration Response with "Pending" status.
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         OutboundFailsAndDisconnectsAfterErtxExpiryForConfigurationResponse) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyPendingConfigRsp.view()},
                        {SignalingChannel::Status::kTimeOut, BufferView()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kTimeOut, BufferView()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) {
      if (open_cb_count == 0) {
        EXPECT_FALSE(chan);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    // FakeSignalingChannel doesn't need to be clocked in order to simulate a timeout.
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenAndLocalCloseChannel) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_TRUE(chan->IsOpen());
        EXPECT_TRUE(chan->IsConnected());
        EXPECT_EQ(kLocalCId, chan->local_cid());
        EXPECT_EQ(kRemoteCId, chan->remote_cid());
        EXPECT_EQ(ChannelMode::kBasic, chan->info().mode);
      }
      open_cb_count++;
    };
  
    int close_cb_count = 0;
    set_channel_close_cb([&close_cb_count](auto chan) {
      EXPECT_TRUE(chan);
      close_cb_count++;
    });
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1, open_cb_count);
    EXPECT_EQ(0, close_cb_count);
  
    bool channel_close_cb_called = false;
    registry()->CloseChannel(kLocalCId, [&] { channel_close_cb_called = true; });
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_EQ(1, open_cb_count);
  
    // Local channel closure shouldn't trigger the close callback.
    EXPECT_EQ(0, close_cb_count);
  
    // Callback passed to CloseChannel should be called nonetheless.
    EXPECT_TRUE(channel_close_cb_called);
  
    // Repeated closure of the same channel should not have any effect.
    channel_close_cb_called = false;
    registry()->CloseChannel(kLocalCId, [&] { channel_close_cb_called = true; });
    EXPECT_TRUE(channel_close_cb_called);
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_EQ(1, open_cb_count);
    EXPECT_EQ(0, close_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenAndRemoteCloseChannel) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) { open_cb_count++; };
  
    int close_cb_count = 0;
    set_channel_close_cb([&close_cb_count](auto chan) {
      ASSERT_TRUE(chan);
      EXPECT_FALSE(chan->IsOpen());
      EXPECT_FALSE(chan->IsConnected());
      EXPECT_EQ(kLocalCId, chan->local_cid());
      EXPECT_EQ(kRemoteCId, chan->remote_cid());
      close_cb_count++;
    });
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1, open_cb_count);
    EXPECT_EQ(0, close_cb_count);
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kDisconnectionRequest, kInboundDisconReq, kInboundDisconRsp));
  
    EXPECT_EQ(1, open_cb_count);
  
    // Remote channel closure should trigger the close callback.
    EXPECT_EQ(1, close_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenChannelWithPendingConn) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kPendingConnRsp.view()},
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    registry()->OpenOutbound(kPsm, kChannelParams, [&open_cb_count](auto chan) {
      open_cb_count++;
      ASSERT_TRUE(chan);
      EXPECT_EQ(kLocalCId, chan->local_cid());
      EXPECT_EQ(kRemoteCId, chan->remote_cid());
    });
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenChannelMismatchConnRsp) {
    // The first Connection Response (pending) has a different ID than the final
    // Connection Response (success).
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kPendingConnRspWithId.view()},
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    registry()->OpenOutbound(kPsm, kChannelParams, [&open_cb_count](auto chan) {
      open_cb_count++;
      ASSERT_TRUE(chan);
      EXPECT_EQ(kLocalCId, chan->local_cid());
      EXPECT_EQ(kRemoteCId, chan->remote_cid());
    });
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenChannelConfigPending) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kOutboundEmptyPendingConfigRsp.view()},
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    registry()->OpenOutbound(kPsm, kChannelParams, [&open_cb_count](auto chan) {
      open_cb_count++;
      ASSERT_TRUE(chan);
      EXPECT_EQ(kLocalCId, chan->local_cid());
      EXPECT_EQ(kRemoteCId, chan->remote_cid());
    });
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenChannelRemoteDisconnectWhileConfiguring) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    auto config_id = EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view());
  
    int open_cb_count = 0;
    registry()->OpenOutbound(kPsm, kChannelParams, [&open_cb_count](auto chan) {
      open_cb_count++;
      EXPECT_FALSE(chan);
    });
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kDisconnectionRequest, kInboundDisconReq, kInboundDisconRsp));
  
    // Response handler should return false ("no more responses") when called, so
    // trigger single responses rather than a set of two.
    RETURN_IF_FATAL(sig()->ReceiveResponses(
        config_id, {{SignalingChannel::Status::kSuccess, kOutboundEmptyPendingConfigRsp.view()}}));
    RETURN_IF_FATAL(sig()->ReceiveResponses(
        config_id, {{SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()}}));
  
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, ChannelIdNotReusedUntilDisconnectionCompletes) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    auto disconn_id = EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view());
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) {
      ASSERT_TRUE(chan);
      open_cb_count++;
    };
  
    int close_cb_count = 0;
    set_channel_close_cb([&close_cb_count](auto chan) {
      EXPECT_TRUE(chan);
      close_cb_count++;
    });
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    // Complete opening the channel.
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1, open_cb_count);
    EXPECT_EQ(0, close_cb_count);
  
    bool channel_close_cb_called = false;
    registry()->CloseChannel(kLocalCId, [&] { channel_close_cb_called = true; });
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    // Disconnection Response hasn't been received yet so the second channel
    // should use a different channel ID.
    const ByteBuffer& kSecondChannelConnReq = CreateStaticByteBuffer(
        // PSM
        LowerBits(kPsm), UpperBits(kPsm),
  
        // Source CID
        LowerBits(kLocalCId + 1), UpperBits(kLocalCId + 1));
  
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kSecondChannelConnReq.view());
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    // CloseChannel callback hasn't been called either.
    EXPECT_FALSE(channel_close_cb_called);
  
    // Complete the disconnection on the first channel.
    RETURN_IF_FATAL(sig()->ReceiveResponses(
        disconn_id, {{SignalingChannel::Status::kSuccess, kDisconRsp.view()}}));
  
    EXPECT_TRUE(channel_close_cb_called);
  
    // Now the first channel ID gets reused.
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view());
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  }
  
  // DisconnectDoneCallback is load-bearing as a signal for the registry to delete a channel's state
  // and recycle its channel ID, so test that it's called even when the peer doesn't actually send a
  // Disconnect Response.
  TEST_F(L2CAP_BrEdrDynamicChannelTest, DisconnectDoneCallbackCalledAfterDisconnectResponseTimeOut) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
  
    // Build channel and operate it directly to be able to disconnect it.
    auto channel =
        BrEdrDynamicChannel::MakeOutbound(registry(), sig(), kPsm, kLocalCId, kChannelParams, false);
    ASSERT_TRUE(channel);
    channel->Open([] {});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_TRUE(channel->IsConnected());
  
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kTimeOut, BufferView()});
  
    bool disconnect_done_cb_called = false;
    channel->Disconnect([&disconnect_done_cb_called] { disconnect_done_cb_called = true; });
    EXPECT_FALSE(channel->IsConnected());
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_TRUE(disconnect_done_cb_called);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, OpenChannelConfigWrongId) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kUnknownIdConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    registry()->OpenOutbound(kPsm, kChannelParams, [&open_cb_count](auto chan) {
      open_cb_count++;
      EXPECT_FALSE(chan);
    });
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(sig()->ReceiveExpectRejectInvalidChannelId(
        kConfigurationRequest, kInboundConfigReq, kLocalCId, kInvalidChannelId));
  
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, InboundConnectionOk) {
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    DynamicChannelCallback open_cb = [&open_cb_count](auto chan) {
      open_cb_count++;
      ASSERT_TRUE(chan);
      EXPECT_EQ(kPsm, chan->psm());
      EXPECT_EQ(kLocalCId, chan->local_cid());
      EXPECT_EQ(kRemoteCId, chan->remote_cid());
    };
  
    int service_request_cb_count = 0;
    auto service_request_cb =
        [&service_request_cb_count, open_cb = std::move(open_cb)](
            PSM psm) mutable -> std::optional<DynamicChannelRegistry::ServiceInfo> {
      service_request_cb_count++;
      EXPECT_EQ(kPsm, psm);
      if (psm == kPsm) {
        return DynamicChannelRegistry::ServiceInfo(kChannelParams, open_cb.share());
      }
      return std::nullopt;
    };
  
    set_service_request_cb(std::move(service_request_cb));
  
    int close_cb_count = 0;
    set_channel_close_cb([&close_cb_count](auto chan) { close_cb_count++; });
  
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConnectionRequest, kInboundConnReq, kInboundOkConnRsp));
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_EQ(1, service_request_cb_count);
    EXPECT_EQ(0, open_cb_count);
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1, service_request_cb_count);
    EXPECT_EQ(1, open_cb_count);
  
    registry()->CloseChannel(kLocalCId, [] {});
    EXPECT_EQ(0, close_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, InboundConnectionRemoteDisconnectWhileConfiguring) {
    auto config_id = EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view());
  
    int open_cb_count = 0;
    DynamicChannelCallback open_cb = [&open_cb_count](auto chan) {
      open_cb_count++;
      FAIL() << "Failed-to-open inbound channels shouldn't trip open callback";
    };
  
    int service_request_cb_count = 0;
    auto service_request_cb =
        [&service_request_cb_count, open_cb = std::move(open_cb)](
            PSM psm) mutable -> std::optional<DynamicChannelRegistry::ServiceInfo> {
      service_request_cb_count++;
      EXPECT_EQ(kPsm, psm);
      if (psm == kPsm) {
        return DynamicChannelRegistry::ServiceInfo(kChannelParams, open_cb.share());
      }
      return std::nullopt;
    };
  
    set_service_request_cb(std::move(service_request_cb));
  
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConnectionRequest, kInboundConnReq, kInboundOkConnRsp));
    RunLoopUntilIdle();
  
    EXPECT_EQ(1, service_request_cb_count);
    EXPECT_EQ(0, open_cb_count);
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kDisconnectionRequest, kInboundDisconReq, kInboundDisconRsp));
  
    // Drop response received after the channel is disconnected.
    RETURN_IF_FATAL(sig()->ReceiveResponses(
        config_id, {{SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()}}));
  
    EXPECT_EQ(1, service_request_cb_count);
  
    // Channel that failed to open shouldn't have triggered channel open callback.
    EXPECT_EQ(0, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, InboundConnectionInvalidPsm) {
    auto service_request_cb = [](PSM psm) -> std::optional<DynamicChannelRegistry::ServiceInfo> {
      // Write user code that accepts the invalid PSM, but control flow may not
      // reach here.
      EXPECT_EQ(kInvalidPsm, psm);
      if (psm == kInvalidPsm) {
        return DynamicChannelRegistry::ServiceInfo(
            kChannelParams, [](auto /*unused*/) { FAIL() << "Channel should fail to open for PSM"; });
      }
      return std::nullopt;
    };
  
    set_service_request_cb(std::move(service_request_cb));
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConnectionRequest, kInboundInvalidPsmConnReq, kInboundBadPsmConnRsp));
    RunLoopUntilIdle();
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, InboundConnectionUnsupportedPsm) {
    int service_request_cb_count = 0;
    auto service_request_cb =
        [&service_request_cb_count](PSM psm) -> std::optional<DynamicChannelRegistry::ServiceInfo> {
      service_request_cb_count++;
      EXPECT_EQ(kPsm, psm);
  
      // Reject the service request.
      return std::nullopt;
    };
  
    set_service_request_cb(std::move(service_request_cb));
  
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConnectionRequest, kInboundConnReq, kInboundBadPsmConnRsp));
    RunLoopUntilIdle();
  
    EXPECT_EQ(1, service_request_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, InboundConnectionInvalidSrcCId) {
    auto service_request_cb = [](PSM psm) -> std::optional<DynamicChannelRegistry::ServiceInfo> {
      // Control flow may not reach here.
      EXPECT_EQ(kPsm, psm);
      if (psm == kPsm) {
        return DynamicChannelRegistry::ServiceInfo(kChannelParams, [](auto /*unused*/) {
          FAIL() << "Channel from src_cid should fail to open";
        });
      }
      return std::nullopt;
    };
  
    set_service_request_cb(std::move(service_request_cb));
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConnectionRequest, kInboundBadCIdConnReq, kInboundBadCIdConnRsp));
    RunLoopUntilIdle();
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, RejectConfigReqWithUnknownOptions) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
  
    size_t open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) { open_cb_count++; };
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    const ByteBuffer& kInboundConfigReqUnknownOption = CreateStaticByteBuffer(
        // Destination CID
        LowerBits(kLocalCId), UpperBits(kLocalCId),
  
        // Flags
        0x00, 0x00,
  
        // Unknown Option: Type, Length, Data
        0x70, 0x01, 0x02);
  
    const ByteBuffer& kOutboundConfigRspUnknownOption = CreateStaticByteBuffer(
        // Source CID
        LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
        // Flags
        0x00, 0x00,
  
        // Result (Failure - unknown options)
        0x03, 0x00,
  
        // Unknown Option: Type, Length, Data
        0x70, 0x01, 0x02);
  
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqUnknownOption,
                                         kOutboundConfigRspUnknownOption));
  
    EXPECT_EQ(0u, open_cb_count);
  
    RunLoopUntilIdle();
  
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, ClampErtmChannelInfoMaxTxSduSizeToMaxPduPayloadSize) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    const auto kPeerMps = 1024;
    const auto kPeerMtu = kPeerMps + 1;
    bool channel_opened = false;
    auto open_cb = [&](auto chan) {
      channel_opened = true;
      ASSERT_TRUE(chan);
      EXPECT_TRUE(chan->IsOpen());
  
      // Note that max SDU size is the peer's MPS because it's smaller than its MTU.
      EXPECT_EQ(kPeerMps, chan->info().max_tx_sdu_size);
    };
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, std::move(open_cb));
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    const auto kInboundConfigReq =
        MakeConfigReqWithMtuAndRfc(kLocalCId, kPeerMtu, ChannelMode::kEnhancedRetransmission,
                                   kErtmNFramesInTxWindow, kErtmMaxTransmissions, 0, 0, kPeerMps);
    const auto kOutboundConfigRsp = MakeConfigRspWithMtuAndRfc(
        kRemoteCId, ConfigurationResult::kSuccess, ChannelMode::kEnhancedRetransmission, kPeerMtu,
        kErtmNFramesInTxWindow, kErtmMaxTransmissions, 2000, 12000, kPeerMps);
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundConfigRsp));
  
    EXPECT_TRUE(channel_opened);
  }
  
  struct ReceiveMtuTestParams {
    std::optional<uint16_t> request_mtu;
    uint16_t response_mtu;
    ConfigurationResult response_status;
  };
  class ReceivedMtuTest : public L2CAP_BrEdrDynamicChannelTest,
                          public ::testing::WithParamInterface<ReceiveMtuTestParams> {};
  
  TEST_P(ReceivedMtuTest, ResponseMtuAndStatus) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
  
    bool channel_opened = false;
    auto open_cb = [&](auto chan) {
      channel_opened = true;
      ASSERT_TRUE(chan);
      EXPECT_TRUE(chan->IsOpen());
      EXPECT_EQ(chan->info().max_tx_sdu_size, GetParam().response_mtu);
    };
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    const auto kOutboundConfigRsp =
        MakeConfigRspWithMtu(kRemoteCId, GetParam().response_mtu, GetParam().response_status);
  
    if (GetParam().request_mtu) {
      RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest,
                                           MakeConfigReqWithMtu(kLocalCId, *GetParam().request_mtu),
                                           kOutboundConfigRsp));
    } else {
      RETURN_IF_FATAL(
          sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundConfigRsp));
    }
  
    EXPECT_EQ(GetParam().response_status == ConfigurationResult::kSuccess, channel_opened);
  
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  }
  
  INSTANTIATE_TEST_SUITE_P(
      L2CAP_BrEdrDynamicChannelTest, ReceivedMtuTest,
      ::testing::Values(
          ReceiveMtuTestParams{std::nullopt, kDefaultMTU, ConfigurationResult::kSuccess},
          ReceiveMtuTestParams{kMinACLMTU, kMinACLMTU, ConfigurationResult::kSuccess},
          ReceiveMtuTestParams{kMinACLMTU - 1, kMinACLMTU,
                               ConfigurationResult::kUnacceptableParameters},
          ReceiveMtuTestParams{kDefaultMTU + 1, kDefaultMTU + 1, ConfigurationResult::kSuccess}));
  
  class ConfigRspWithMtuTest
      : public L2CAP_BrEdrDynamicChannelTest,
        public ::testing::WithParamInterface<std::optional<uint16_t> /*response mtu*/> {};
  
  TEST_P(ConfigRspWithMtuTest, ConfiguredLocalMtu) {
    const auto kExpectedConfiguredLocalMtu = GetParam() ? *GetParam() : kMaxMTU;
  
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
  
    const auto kInboundConfigRspWithParamMtu =
        MakeConfigRspWithMtu(kLocalCId, GetParam() ? *GetParam() : 0);
    if (GetParam()) {
      EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                          {SignalingChannel::Status::kSuccess, kInboundConfigRspWithParamMtu.view()});
    } else {
      EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                          {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    }
  
    size_t open_cb_count = 0;
    auto open_cb = [&](auto chan) {
      EXPECT_TRUE(chan->IsOpen());
      EXPECT_EQ(kExpectedConfiguredLocalMtu, chan->info().max_rx_sdu_size);
      open_cb_count++;
    };
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1u, open_cb_count);
  
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  }
  
  TEST_P(ConfigRspWithMtuTest, ConfiguredLocalMtuWithPendingRsp) {
    const auto kExpectedConfiguredLocalMtu = GetParam() ? *GetParam() : kMaxMTU;
  
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
  
    const auto kInboundPendingConfigRspWithMtu =
        MakeConfigRspWithMtu(kLocalCId, GetParam() ? *GetParam() : 0, ConfigurationResult::kPending);
    if (GetParam()) {
      EXPECT_OUTBOUND_REQ(
          *sig(), kConfigurationRequest, kOutboundConfigReq.view(),
          {SignalingChannel::Status::kSuccess, kInboundPendingConfigRspWithMtu.view()},
          {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    } else {
      EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                          {SignalingChannel::Status::kSuccess, kInboundEmptyPendingConfigRsp.view()},
                          {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    }
  
    size_t open_cb_count = 0;
    auto open_cb = [&](auto chan) {
      EXPECT_TRUE(chan->IsOpen());
      EXPECT_EQ(kExpectedConfiguredLocalMtu, chan->info().max_rx_sdu_size);
      open_cb_count++;
    };
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    EXPECT_EQ(1u, open_cb_count);
  
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  }
  
  INSTANTIATE_TEST_SUITE_P(L2CAP_BrEdrDynamicChannelTest, ConfigRspWithMtuTest,
                           ::testing::Values(std::nullopt, kMinACLMTU));
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, RespondsToInboundExtendedFeaturesRequest) {
    const auto kExpectedExtendedFeatures =
        kExtendedFeaturesBitFixedChannels | kExtendedFeaturesBitEnhancedRetransmission;
    const auto kExpectedExtendedFeaturesInfoRsp =
        MakeExtendedFeaturesInfoRsp(InformationResult::kSuccess, kExpectedExtendedFeatures);
    sig()->ReceiveExpect(kInformationRequest, kExtendedFeaturesInfoReq,
                         kExpectedExtendedFeaturesInfoRsp);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, ExtendedFeaturesResponseSaved) {
    const auto kExpectedExtendedFeatures =
        kExtendedFeaturesBitFixedChannels | kExtendedFeaturesBitEnhancedRetransmission;
    const auto kInfoRsp =
        MakeExtendedFeaturesInfoRsp(InformationResult::kSuccess, kExpectedExtendedFeatures);
  
    EXPECT_FALSE(registry()->extended_features());
  
    sig()->ReceiveResponses(ext_info_transaction_id(),
                            {{SignalingChannel::Status::kSuccess, kInfoRsp.view()}});
    EXPECT_TRUE(registry()->extended_features());
    EXPECT_EQ(kExpectedExtendedFeatures, *registry()->extended_features());
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, ExtendedFeaturesResponseInvalidFailureResult) {
    constexpr auto kResult = static_cast<InformationResult>(0xFFFF);
    const auto kInfoRsp = MakeExtendedFeaturesInfoRsp(kResult);
  
    EXPECT_FALSE(registry()->extended_features());
  
    sig()->ReceiveResponses(ext_info_transaction_id(),
                            {{SignalingChannel::Status::kSuccess, kInfoRsp.view()}});
    EXPECT_FALSE(registry()->extended_features());
  }
  
  class InformationResultTest : public L2CAP_BrEdrDynamicChannelTest,
                                public ::testing::WithParamInterface<InformationResult> {};
  
  TEST_P(InformationResultTest,
         ERTMChannelWaitsForExtendedFeaturesResultBeforeFallingBackToBasicModeAndStartingConfigFlow) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
  
    size_t open_cb_count = 0;
    auto open_cb = [&open_cb_count](auto chan) { open_cb_count++; };
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, std::move(open_cb));
  
    // Config request should not be sent.
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
  
    const auto kExtendedFeaturesInfoRsp = MakeExtendedFeaturesInfoRsp(GetParam());
    sig()->ReceiveResponses(ext_info_transaction_id(),
                            {{SignalingChannel::Status::kSuccess, kExtendedFeaturesInfoRsp.view()}});
  
    RunLoopUntilIdle();
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    // Config should have been sent, so channel should be open.
    EXPECT_EQ(1u, open_cb_count);
  
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  }
  
  INSTANTIATE_TEST_SUITE_P(L2CAP_BrEdrDynamicChannelTest, InformationResultTest,
                           ::testing::Values(InformationResult::kSuccess,
                                             InformationResult::kNotSupported,
                                             static_cast<InformationResult>(0xFFFF)));
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, ERTChannelDoesNotSendConfigReqBeforeConnRspReceived) {
    auto conn_id = EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(), {});
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, {});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    // Channel will be notified that extended features received.
    sig()->ReceiveResponses(ext_info_transaction_id(),
                            {{SignalingChannel::Status::kSuccess, kExtendedFeaturesInfoRsp.view()}});
  
    // Config request should not be sent before connection response received.
    RunLoopUntilIdle();
  
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    sig()->ReceiveResponses(conn_id, {{SignalingChannel::Status::kSuccess, kOkConnRsp.view()}});
    RunLoopUntilIdle();
  
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, SendAndReceiveERTMConfigReq) {
    constexpr uint16_t kPreferredMtu = kDefaultMTU + 1;
    const auto kExpectedOutboundConfigReq = MakeConfigReqWithMtuAndRfc(
        kRemoteCId, kPreferredMtu, ChannelMode::kEnhancedRetransmission, kErtmMaxUnackedInboundFrames,
        kErtmMaxInboundRetransmissions, 0, 0, kMaxInboundPduPayloadSize);
  
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kExpectedOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
  
    auto open_cb = [kPreferredMtu, &open_cb_count](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_TRUE(chan->IsOpen());
  
        // Check values of ChannelInfo fields.
        EXPECT_EQ(ChannelMode::kEnhancedRetransmission, chan->info().mode);
  
        // Receive capability even under ERTM is based on MTU option, not on the MPS in R&FC option.
        EXPECT_EQ(kPreferredMtu, chan->info().max_rx_sdu_size);
  
        // Inbound request has no MTU option, so the peer's receive capability is the default.
        EXPECT_EQ(kDefaultMTU, chan->info().max_tx_sdu_size);
  
        // These values should match the contents of kInboundConfigReqWithERTM.
        EXPECT_EQ(kErtmNFramesInTxWindow, chan->info().n_frames_in_tx_window);
        EXPECT_EQ(kErtmMaxTransmissions, chan->info().max_transmissions);
        EXPECT_EQ(kMaxTxPduPayloadSize, chan->info().max_tx_pdu_payload_size);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, {ChannelMode::kEnhancedRetransmission, kPreferredMtu},
                             std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
  
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqWithERTM,
                                         kOutboundOkConfigRspWithErtm));
  
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  }
  
  // When the peer rejects ERTM with the result Unacceptable Parameters and the R&FC
  // option specifying basic mode, the local device should send a new request with basic mode.
  // When the peer then requests basic mode, it should be accepted.
  // PTS: L2CAP/CMC/BV-03-C
  TEST_F(L2CAP_BrEdrDynamicChannelTest, PeerRejectsERTM) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(
        *sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view(),
        {SignalingChannel::Status::kSuccess, kInboundUnacceptableParamsWithRfcBasicConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
  
    auto open_cb = [&open_cb_count](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_TRUE(chan->IsOpen());
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
  
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  }
  
  // Local device that prefers ERTM will renegotiate channel mode to basic mode after peer negotiates
  // basic mode and rejects ERTM.
  // PTS: L2CAP/CMC/BV-07-C
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         RenegotiateChannelModeAfterPeerRequestsBasicModeAndRejectsERTM) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    auto config_req_id =
        EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view());
  
    int open_cb_count = 0;
  
    auto open_cb = [&open_cb_count](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_TRUE(chan->IsOpen());
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, std::move(open_cb));
  
    RunLoopUntilIdle();
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
    RunLoopUntilIdle();
  
    // Peer requests basic mode.
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
    RunLoopUntilIdle();
  
    // New config request requesting basic mode should be sent in response to unacceptable params
    // response.
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    sig()->ReceiveResponses(config_req_id,
                            {{SignalingChannel::Status::kSuccess,
                              kInboundUnacceptableParamsWithRfcBasicConfigRsp.view()}});
  
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  }
  
  // The local device should configure basic mode if peer does not indicate support for ERTM when it
  // is preferred.
  // PTS: L2CAP/CMC/BV-10-C
  TEST_F(L2CAP_BrEdrDynamicChannelTest, PreferredModeIsERTMButERTMIsNotInPeerFeatureMask) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, {});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    // Receive features mask without ERTM bit set.
    sig()->ReceiveResponses(ext_info_transaction_id(),
                            {{SignalingChannel::Status::kSuccess, kExtendedFeaturesInfoRsp.view()}});
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, RejectERTMRequestWhenPreferredModeIsBasic) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    registry()->OpenOutbound(kPsm, kChannelParams, {});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    // Peer requests ERTM. Local device should reject with unacceptable params.
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqWithERTM,
                                         kOutboundUnacceptableParamsWithRfcBasicConfigRsp));
  }
  
  // Core Spec v5.1, Vol 3, Part A, Sec 5.4:
  // If the mode in the remote device's negative Configuration Response does
  // not match the mode in the remote device's Configuration Request then the
  // local device shall disconnect the channel.
  //
  // Inbound config request received BEFORE outbound config request:
  // <- ConfigurationRequest (with ERTM)
  // -> ConfigurationResponse (Ok)
  // -> ConfigurationRequest (with ERTM)
  // <- ConfigurationResponse (Unacceptable, with Basic)
  TEST_F(
      L2CAP_BrEdrDynamicChannelTest,
      DisconnectWhenInboundConfigReqReceivedBeforeOutboundConfigReqSentModeInInboundUnacceptableParamsConfigRspDoesNotMatchPeerConfigReq) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(
        *sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view(),
        {SignalingChannel::Status::kSuccess, kInboundUnacceptableParamsWithRfcBasicConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        EXPECT_FALSE(chan);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    // Receive inbound config request.
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqWithERTM,
                                         kOutboundOkConfigRspWithErtm));
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
    // Send outbound config request.
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  }
  
  // Same as above, but inbound config request received AFTER outbound configuration request:
  // -> ConfigurationRequest (with ERTM)
  // <- ConfigurationRequest (with ERTM)
  // -> ConfigurationResponse (Ok)
  // <- ConfigurationResponse (Unacceptable, with Basic)
  TEST_F(
      L2CAP_BrEdrDynamicChannelTest,
      DisconnectWhenInboundConfigReqReceivedAfterOutboundConfigReqSentAndModeInInboundUnacceptableParamsConfigRspDoesNotMatchPeerConfigReq) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    const auto outbound_config_req_id =
        EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view());
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        EXPECT_FALSE(chan);
      }
      open_cb_count++;
    };
  
    ChannelParameters params;
    params.mode = ChannelMode::kEnhancedRetransmission;
    registry()->OpenOutbound(kPsm, params, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
    // Send outbound config request.
    RunLoopUntilIdle();
  
    // Receive inbound config request.
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqWithERTM,
                                         kOutboundOkConfigRspWithErtm));
  
    sig()->ReceiveResponses(outbound_config_req_id,
                            {{SignalingChannel::Status::kSuccess,
                              kInboundUnacceptableParamsWithRfcBasicConfigRsp.view()}});
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, DisconnectAfterReceivingTwoConfigRequestsWithoutDesiredMode) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        EXPECT_FALSE(chan);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqWithERTM,
                                         kOutboundUnacceptableParamsWithRfcBasicConfigRsp));
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqWithERTM,
                                         kOutboundUnacceptableParamsWithRfcBasicConfigRsp));
  
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, RetryWhenPeerRejectsConfigReqWithBasicMode) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(
        *sig(), kConfigurationRequest, kOutboundConfigReq.view(),
        {SignalingChannel::Status::kSuccess, kInboundUnacceptableParamsWithRfcBasicConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    auto open_cb = [&open_cb_count](const DynamicChannel* chan) { open_cb_count++; };
  
    registry()->OpenOutbound(kPsm, kChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    RunLoopUntilIdle();
    EXPECT_EQ(0, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         SendUnacceptableParamsResponseWhenPeerRequestsUnsupportedChannelMode) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, {});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    // Retransmission mode is not supported.
    const auto kInboundConfigReqWithRetransmissionMode =
        MakeConfigReqWithMtuAndRfc(kLocalCId, kMaxMTU, ChannelMode::kRetransmission, 0, 0, 0, 0, 0);
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest,
                                         kInboundConfigReqWithRetransmissionMode,
                                         kOutboundUnacceptableParamsWithRfcBasicConfigRsp));
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         SendUnacceptableParamsResponseWhenPeerRequestsUnsupportedChannelModeAndSupportsErtm) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view(), {});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, {});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    // Retransmission mode is not supported.
    const auto kInboundConfigReqWithRetransmissionMode =
        MakeConfigReqWithMtuAndRfc(kLocalCId, kMaxMTU, ChannelMode::kRetransmission, 0, 0, 0, 0, 0);
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest,
                                         kInboundConfigReqWithRetransmissionMode,
                                         kOutboundUnacceptableParamsWithRfcERTMConfigRsp));
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         SendUnacceptableParamsResponseWhenPeerRequestErtmWithZeroTxWindow) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, {});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    constexpr uint8_t kMaxTransmit = 31;
    constexpr auto kMps = kMaxTxPduPayloadSize;
  
    // TxWindow of zero is out of range.
    const auto kInboundConfigReqWithZeroTxWindow =
        MakeConfigReqWithMtuAndRfc(kLocalCId, kDefaultMTU, ChannelMode::kEnhancedRetransmission,
                                   /*tx_window=*/0, /*max_transmit=*/kMaxTransmit,
                                   /*retransmission_timeout=*/0, /*monitor_timeout=*/0, /*mps=*/kMps);
    const auto kOutboundConfigRsp = MakeConfigRspWithRfc(
        kRemoteCId, ConfigurationResult::kUnacceptableParameters,
        ChannelMode::kEnhancedRetransmission, /*tx_window=*/1, /*max_transmit=*/kMaxTransmit,
        /*retransmission_timeout=*/0, /*monitor_timeout=*/0, /*mps=*/kMps);
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqWithZeroTxWindow,
                                         kOutboundConfigRsp));
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         SendUnacceptableParamsResponseWhenPeerRequestErtmWithOversizeTxWindow) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, {});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    constexpr uint8_t kMaxTransmit = 31;
    constexpr auto kMps = kMaxTxPduPayloadSize;
  
    // TxWindow of 200 is out of range.
    const auto kInboundConfigReqWithOversizeTxWindow =
        MakeConfigReqWithMtuAndRfc(kLocalCId, kDefaultMTU, ChannelMode::kEnhancedRetransmission,
                                   /*tx_window=*/200, /*max_transmit=*/kMaxTransmit,
                                   /*retransmission_timeout=*/0, /*monitor_timeout=*/0, /*mps=*/kMps);
    const auto kOutboundConfigRsp = MakeConfigRspWithRfc(
        kRemoteCId, ConfigurationResult::kUnacceptableParameters,
        ChannelMode::kEnhancedRetransmission, /*tx_window=*/63, /*max_transmit=*/kMaxTransmit,
        /*retransmission_timeout=*/0, /*monitor_timeout=*/0, /*mps=*/kMps);
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqWithOversizeTxWindow,
                                         kOutboundConfigRsp));
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         SendUnacceptableParamsResponseWhenPeerRequestErtmWithUndersizeMps) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, {});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    constexpr uint8_t kMaxTransmit = 31;
    constexpr uint8_t kTxWindow = kErtmMaxUnackedInboundFrames;
  
    // MPS of 16 would not be able to fit a 48-byte (minimum MTU) SDU without segmentation.
    const auto kInboundConfigReqWithUndersizeMps =
        MakeConfigReqWithMtuAndRfc(kLocalCId, kDefaultMTU, ChannelMode::kEnhancedRetransmission,
                                   /*tx_window=*/kTxWindow, /*max_transmit=*/kMaxTransmit,
                                   /*retransmission_timeout=*/0, /*monitor_timeout=*/0, /*mps=*/16);
    const auto kOutboundConfigRsp = MakeConfigRspWithRfc(
        kRemoteCId, ConfigurationResult::kUnacceptableParameters,
        ChannelMode::kEnhancedRetransmission, /*tx_window=*/kTxWindow, /*max_transmit=*/kMaxTransmit,
        /*retransmission_timeout=*/0, /*monitor_timeout=*/0, /*mps=*/kMinACLMTU);
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqWithUndersizeMps,
                                         kOutboundConfigRsp));
  }
  
  // Local config with ERTM incorrectly accepted by peer, then peer requests basic mode which
  // the local device must accept. These modes are incompatible, so the local device should
  // default to Basic Mode.
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         OpenBasicModeChannelAfterPeerAcceptsErtmThenPeerRequestsBasicMode) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
  
    auto open_cb = [&open_cb_count](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_EQ(ChannelMode::kBasic, chan->info().mode);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
  
    // Request ERTM.
    RunLoopUntilIdle();
  
    // Peer requests basic mode.
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    // Disconnect
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  }
  
  // Same as above, but the peer sends its positive response after sending its Basic Mode request.
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         OpenBasicModeChannelAfterPeerRequestsBasicModeThenPeerAcceptsErtm) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
  
    auto open_cb = [&open_cb_count](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_EQ(ChannelMode::kBasic, chan->info().mode);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, kERTMChannelParams, std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    // Peer requests basic mode.
    RETURN_IF_FATAL(
        sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp));
  
    // Local device will request ERTM.
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
    // Request ERTM & Disconnect
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, MtuChannelParameterSentInConfigReq) {
    constexpr uint16_t kPreferredMtu = kDefaultMTU + 1;
    const auto kExpectedOutboundConfigReq = MakeConfigReqWithMtu(kRemoteCId, kPreferredMtu);
  
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kExpectedOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    auto open_cb = [&](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_EQ(kPreferredMtu, chan->info().max_rx_sdu_size);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, {ChannelMode::kBasic, kPreferredMtu}, open_cb);
    RunLoopUntilIdle();
  
    sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp);
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, UseMinMtuWhenMtuChannelParameterIsBelowMin) {
    constexpr uint16_t kMtu = kMinACLMTU - 1;
    const auto kExpectedOutboundConfigReq = MakeConfigReqWithMtu(kRemoteCId, kMinACLMTU);
  
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kExpectedOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    auto open_cb = [&](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_EQ(kMinACLMTU, chan->info().max_rx_sdu_size);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, {ChannelMode::kBasic, kMtu}, open_cb);
    RunLoopUntilIdle();
  
    sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kOutboundOkConfigRsp);
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         UseMaxPduPayloadSizeWhenMtuChannelParameterExceedsItWithErtm) {
    constexpr uint16_t kPreferredMtu = kMaxInboundPduPayloadSize + 1;
    const auto kExpectedOutboundConfigReq =
        MakeConfigReqWithMtuAndRfc(kRemoteCId, kMaxInboundPduPayloadSize,
                                   ChannelMode::kEnhancedRetransmission, kErtmMaxUnackedInboundFrames,
                                   kErtmMaxInboundRetransmissions, 0, 0, kMaxInboundPduPayloadSize);
  
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kExpectedOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    int open_cb_count = 0;
    auto open_cb = [&](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_EQ(kMaxInboundPduPayloadSize, chan->info().max_rx_sdu_size);
      }
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, {ChannelMode::kEnhancedRetransmission, kPreferredMtu},
                             std::move(open_cb));
  
    RETURN_IF_FATAL(RunLoopUntilIdle());
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
  
    RETURN_IF_FATAL(sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqWithERTM,
                                         kOutboundOkConfigRspWithErtm));
  
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         BasicModeChannelReportsChannelInfoWithBasicModeAndSduCapacities) {
    constexpr uint16_t kPreferredMtu = kDefaultMTU + 1;
    const auto kExpectedOutboundConfigReq = MakeConfigReqWithMtu(kRemoteCId, kPreferredMtu);
  
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kExpectedOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    constexpr uint16_t kPeerMtu = kDefaultMTU + 2;
    const auto kInboundConfigReq = MakeConfigReqWithMtu(kLocalCId, kPeerMtu);
  
    int open_cb_count = 0;
    auto open_cb = [&](const DynamicChannel* chan) {
      ASSERT_TRUE(chan);
      EXPECT_EQ(ChannelMode::kBasic, chan->info().mode);
      EXPECT_EQ(kPreferredMtu, chan->info().max_rx_sdu_size);
      EXPECT_EQ(kPeerMtu, chan->info().max_tx_sdu_size);
      open_cb_count++;
    };
  
    registry()->OpenOutbound(kPsm, {ChannelMode::kBasic, kPreferredMtu}, open_cb);
    RunLoopUntilIdle();
  
    const ByteBuffer& kExpectedOutboundOkConfigRsp = MakeConfigRspWithMtu(kRemoteCId, kPeerMtu);
    sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq, kExpectedOutboundOkConfigRsp);
    RunLoopUntilIdle();
    EXPECT_EQ(1, open_cb_count);
  }
  
  TEST_F(L2CAP_BrEdrDynamicChannelTest, Receive2ConfigReqsWithContinuationFlagInFirstReq) {
    constexpr uint16_t kTxMtu = kMinACLMTU;
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReqWithErtm.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    const auto kInboundConfigReq0 =
        MakeConfigReqWithMtu(kLocalCId, kTxMtu, kConfigurationContinuation);
    const auto kOutboundConfigRsp1 = MakeConfigRspWithMtuAndRfc(
        kRemoteCId, ConfigurationResult::kSuccess, ChannelMode::kEnhancedRetransmission, kTxMtu,
        kErtmNFramesInTxWindow, kErtmMaxTransmissions, 2000, 12000, kMaxTxPduPayloadSize);
  
    size_t open_cb_count = 0;
    auto open_cb = [&](const DynamicChannel* chan) {
      if (open_cb_count == 0) {
        ASSERT_TRUE(chan);
        EXPECT_EQ(kTxMtu, chan->info().max_tx_sdu_size);
        EXPECT_EQ(ChannelMode::kEnhancedRetransmission, chan->info().mode);
      }
      open_cb_count++;
    };
  
    sig()->ReceiveResponses(ext_info_transaction_id(), {{SignalingChannel::Status::kSuccess,
                                                         kExtendedFeaturesInfoRspWithERTM.view()}});
    registry()->OpenOutbound(kPsm, kERTMChannelParams, open_cb);
    RunLoopUntilIdle();
  
    sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq0,
                         kOutboundEmptyContinuationConfigRsp);
    RunLoopUntilIdle();
    EXPECT_EQ(0u, open_cb_count);
  
    sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReqWithERTM, kOutboundConfigRsp1);
    RunLoopUntilIdle();
    EXPECT_EQ(1u, open_cb_count);
  }
  
  // The unknown options from both configuration requests should be included when responding
  // with the "unkown options" result.
  TEST_F(L2CAP_BrEdrDynamicChannelTest,
         Receive2ConfigReqsWithContinuationFlagInFirstReqAndUnknownOptionInBothReqs) {
    EXPECT_OUTBOUND_REQ(*sig(), kConnectionRequest, kConnReq.view(),
                        {SignalingChannel::Status::kSuccess, kOkConnRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kConfigurationRequest, kOutboundConfigReq.view(),
                        {SignalingChannel::Status::kSuccess, kInboundEmptyConfigRsp.view()});
    EXPECT_OUTBOUND_REQ(*sig(), kDisconnectionRequest, kDisconReq.view(),
                        {SignalingChannel::Status::kSuccess, kDisconRsp.view()});
  
    constexpr uint8_t kUnknownOption0Type = 0x70;
    constexpr uint8_t kUnknownOption1Type = 0x71;
    const auto kInboundConfigReq0 = StaticByteBuffer(
        // Destination CID
        LowerBits(kLocalCId), UpperBits(kLocalCId),
        // Flags (C = 1)
        0x01, 0x00,
        // Unknown Option
        kUnknownOption0Type, 0x01, 0x00);
    const auto kInboundConfigReq1 = StaticByteBuffer(
        // Destination CID
        LowerBits(kLocalCId), UpperBits(kLocalCId),
        // Flags (C = 0)
        0x00, 0x00,
        // Unknown Option
        kUnknownOption1Type, 0x01, 0x00);
  
    const auto kOutboundUnknownOptionsConfigRsp = StaticByteBuffer(
        // Source CID
        LowerBits(kRemoteCId), UpperBits(kRemoteCId),
        // Flags (C = 0)
        0x00, 0x00,
        // Result
        LowerBits(static_cast<uint16_t>(ConfigurationResult::kUnknownOptions)),
        UpperBits(static_cast<uint16_t>(ConfigurationResult::kUnknownOptions)),
        // Unknown Options
        kUnknownOption0Type, 0x01, 0x00, kUnknownOption1Type, 0x01, 0x00);
  
    size_t open_cb_count = 0;
    auto open_cb = [&](const DynamicChannel* chan) { open_cb_count++; };
  
    registry()->OpenOutbound(kPsm, kChannelParams, open_cb);
    RunLoopUntilIdle();
  
    sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq0,
                         kOutboundEmptyContinuationConfigRsp);
    RunLoopUntilIdle();
    EXPECT_EQ(0u, open_cb_count);
  
    sig()->ReceiveExpect(kConfigurationRequest, kInboundConfigReq1, kOutboundUnknownOptionsConfigRsp);
    RunLoopUntilIdle();
    EXPECT_EQ(0u, open_cb_count);
  }
  
  }  // namespace
  }  // namespace bt::l2cap::internal
  