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_command_handler.h"
  
  #include <lib/async/cpp/task.h>
  
  #include <memory>
  #include <unordered_map>
  #include <vector>
  
  #include "lib/gtest/test_loop_fixture.h"
  #include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
  #include "src/connectivity/bluetooth/core/bt-host/l2cap/channel_configuration.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 {
  namespace l2cap {
  namespace internal {
  namespace {
  
  using TestBase = ::gtest::TestLoopFixture;
  
  constexpr uint16_t kPsm = 0x0001;
  constexpr ChannelId kLocalCId = 0x0040;
  constexpr ChannelId kRemoteCId = 0x60a3;
  
  class L2CAP_BrEdrCommandHandlerTest : public TestBase {
   public:
    L2CAP_BrEdrCommandHandlerTest() = default;
    ~L2CAP_BrEdrCommandHandlerTest() override = default;
    DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(L2CAP_BrEdrCommandHandlerTest);
  
   protected:
    // TestLoopFixture overrides
    void SetUp() override {
      TestBase::SetUp();
      signaling_channel_ = std::make_unique<testing::FakeSignalingChannel>(dispatcher());
      command_handler_ = std::make_unique<BrEdrCommandHandler>(
          fake_sig(), fit::bind_member(this, &L2CAP_BrEdrCommandHandlerTest::OnRequestFail));
      request_fail_callback_ = nullptr;
      failed_requests_ = 0;
    }
  
    void TearDown() override {
      request_fail_callback_ = nullptr;
      signaling_channel_ = nullptr;
      command_handler_ = nullptr;
      TestBase::TearDown();
    }
  
    testing::FakeSignalingChannel* fake_sig() const { return signaling_channel_.get(); }
    BrEdrCommandHandler* cmd_handler() const { return command_handler_.get(); }
    size_t failed_requests() const { return failed_requests_; }
  
    void set_request_fail_callback(fit::closure request_fail_callback) {
      ZX_ASSERT(!request_fail_callback_);
      request_fail_callback_ = std::move(request_fail_callback);
    }
  
   private:
    void OnRequestFail() {
      failed_requests_++;
      if (request_fail_callback_) {
        request_fail_callback_();
      }
    }
  
    std::unique_ptr<testing::FakeSignalingChannel> signaling_channel_;
    std::unique_ptr<BrEdrCommandHandler> command_handler_;
    fit::closure request_fail_callback_;
    size_t failed_requests_;
  };
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConnReqRej) {
    constexpr ChannelId kBadLocalCId = 0x0005;  // Not a dynamic channel
  
    // Connection Request payload
    auto expected_conn_req = CreateStaticByteBuffer(
        // PSM
        LowerBits(kPsm), UpperBits(kPsm),
  
        // Source CID
        LowerBits(kBadLocalCId), UpperBits(kBadLocalCId));
  
    // Command Reject payload
    auto rej_rsp = CreateStaticByteBuffer(
        // Reject Reason (invalid channel ID)
        LowerBits(static_cast<uint16_t>(RejectReason::kInvalidCID)),
        UpperBits(static_cast<uint16_t>(RejectReason::kInvalidCID)),
  
        // Local (relative to rejecter) CID
        LowerBits(kInvalidChannelId), UpperBits(kInvalidChannelId),
  
        // Remote (relative to rejecter) CID
        LowerBits(kBadLocalCId), UpperBits(kBadLocalCId));
    EXPECT_OUTBOUND_REQ(*fake_sig(), kConnectionRequest, expected_conn_req.view(),
                        {SignalingChannel::Status::kReject, rej_rsp.view()});
  
    bool cb_called = false;
    auto on_conn_rsp = [&cb_called,
                        kBadLocalCId](const BrEdrCommandHandler::ConnectionResponse& rsp) {
      cb_called = true;
      EXPECT_EQ(BrEdrCommandHandler::Status::kReject, rsp.status());
      EXPECT_EQ(kInvalidChannelId, rsp.remote_cid());
      EXPECT_EQ(kBadLocalCId, rsp.local_cid());
      return BrEdrCommandHandler::ResponseHandlerAction::kCompleteOutboundTransaction;
    };
  
    EXPECT_TRUE(cmd_handler()->SendConnectionRequest(kPsm, kBadLocalCId, std::move(on_conn_rsp)));
    RunLoopUntilIdle();
    EXPECT_TRUE(cb_called);
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConnReqRejNotEnoughBytesInRejection) {
    constexpr ChannelId kBadLocalCId = 0x0005;  // Not a dynamic channel
  
    // Connection Request payload
    auto expected_conn_req = CreateStaticByteBuffer(
        // PSM
        LowerBits(kPsm), UpperBits(kPsm),
  
        // Source CID
        LowerBits(kBadLocalCId), UpperBits(kBadLocalCId));
  
    // Command Reject payload (the invalid channel IDs are missing)
    auto rej_rsp = CreateStaticByteBuffer(
        // Reject Reason (invalid channel ID)
        LowerBits(static_cast<uint16_t>(RejectReason::kInvalidCID)),
        UpperBits(static_cast<uint16_t>(RejectReason::kInvalidCID)));
  
    EXPECT_OUTBOUND_REQ(*fake_sig(), kConnectionRequest, expected_conn_req.view(),
                        {SignalingChannel::Status::kReject, rej_rsp.view()});
  
    bool cb_called = false;
    auto on_conn_rsp = [&cb_called](const BrEdrCommandHandler::ConnectionResponse& rsp) {
      cb_called = true;
      return BrEdrCommandHandler::ResponseHandlerAction::kCompleteOutboundTransaction;
    };
  
    EXPECT_TRUE(cmd_handler()->SendConnectionRequest(kPsm, kBadLocalCId, std::move(on_conn_rsp)));
    RunLoopUntilIdle();
    EXPECT_FALSE(cb_called);
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConnReqRspOk) {
    // Connection Request payload
    auto expected_conn_req = CreateStaticByteBuffer(
        // PSM
        LowerBits(kPsm), UpperBits(kPsm),
  
        // Source CID
        LowerBits(kLocalCId), UpperBits(kLocalCId));
  
    // Connection Response payload
    auto ok_conn_rsp = CreateStaticByteBuffer(
        // Destination CID
        LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
        // Source CID
        LowerBits(kLocalCId), UpperBits(kLocalCId),
  
        // Result (Successful)
        0x00, 0x00,
  
        // Status (No further information available)
        0x00, 0x00);
    EXPECT_OUTBOUND_REQ(*fake_sig(), kConnectionRequest, expected_conn_req.view(),
                        {SignalingChannel::Status::kSuccess, ok_conn_rsp.view()});
  
    bool cb_called = false;
    auto on_conn_rsp = [&cb_called](const BrEdrCommandHandler::ConnectionResponse& rsp) {
      cb_called = true;
      EXPECT_EQ(BrEdrCommandHandler::Status::kSuccess, rsp.status());
      EXPECT_EQ(kRemoteCId, rsp.remote_cid());
      EXPECT_EQ(kLocalCId, rsp.local_cid());
      EXPECT_EQ(ConnectionResult::kSuccess, rsp.result());
      EXPECT_EQ(ConnectionStatus::kNoInfoAvailable, rsp.conn_status());
      return SignalingChannel::ResponseHandlerAction::kCompleteOutboundTransaction;
    };
  
    EXPECT_TRUE(cmd_handler()->SendConnectionRequest(kPsm, kLocalCId, std::move(on_conn_rsp)));
    RunLoopUntilIdle();
    EXPECT_TRUE(cb_called);
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConnReqRspPendingAuthThenOk) {
    // Connection Request payload
    auto expected_conn_req = CreateStaticByteBuffer(
        // PSM
        LowerBits(kPsm), UpperBits(kPsm),
  
        // Source CID
        LowerBits(kLocalCId), UpperBits(kLocalCId));
  
    // Connection Response payload
    auto pend_conn_rsp = CreateStaticByteBuffer(
        // Destination CID
        LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
        // Source CID
        LowerBits(kLocalCId), UpperBits(kLocalCId),
  
        // Result (Pending)
        0x01, 0x00,
  
        // Status (Authorization pending)
        0x02, 0x00);
  
    auto ok_conn_rsp = CreateStaticByteBuffer(
        // Destination CID
        LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
        // Source CID
        LowerBits(kLocalCId), UpperBits(kLocalCId),
  
        // Result (Successful)
        0x00, 0x00,
  
        // Status (No further information available)
        0x00, 0x00);
    EXPECT_OUTBOUND_REQ(*fake_sig(), kConnectionRequest, expected_conn_req.view(),
                        {SignalingChannel::Status::kSuccess, pend_conn_rsp.view()},
                        {SignalingChannel::Status::kSuccess, ok_conn_rsp.view()});
  
    int cb_count = 0;
    auto on_conn_rsp = [&cb_count](const BrEdrCommandHandler::ConnectionResponse& rsp) {
      cb_count++;
      EXPECT_EQ(kRemoteCId, rsp.remote_cid());
      EXPECT_EQ(kLocalCId, rsp.local_cid());
      if (cb_count == 1) {
        EXPECT_EQ(BrEdrCommandHandler::Status::kSuccess, rsp.status());
        EXPECT_EQ(ConnectionResult::kPending, rsp.result());
        EXPECT_EQ(ConnectionStatus::kAuthorizationPending, rsp.conn_status());
        return SignalingChannel::ResponseHandlerAction::kExpectAdditionalResponse;
      } else if (cb_count == 2) {
        EXPECT_EQ(BrEdrCommandHandler::Status::kSuccess, rsp.status());
        EXPECT_EQ(ConnectionResult::kSuccess, rsp.result());
        EXPECT_EQ(ConnectionStatus::kNoInfoAvailable, rsp.conn_status());
      }
      return SignalingChannel::ResponseHandlerAction::kCompleteOutboundTransaction;
    };
  
    EXPECT_TRUE(cmd_handler()->SendConnectionRequest(kPsm, kLocalCId, std::move(on_conn_rsp)));
    RunLoopUntilIdle();
    EXPECT_EQ(2, cb_count);
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConnReqRspTimeOut) {
    // Connection Request payload
    auto expected_conn_req = CreateStaticByteBuffer(
        // PSM
        LowerBits(kPsm), UpperBits(kPsm),
  
        // Source CID
        LowerBits(kLocalCId), UpperBits(kLocalCId));
  
    EXPECT_OUTBOUND_REQ(*fake_sig(), kConnectionRequest, expected_conn_req.view(),
                        {SignalingChannel::Status::kTimeOut, {}});
  
    ASSERT_EQ(0u, failed_requests());
  
    auto on_conn_rsp = [](auto&) {
      ADD_FAILURE();
      return SignalingChannel::ResponseHandlerAction::kCompleteOutboundTransaction;
    };
    EXPECT_TRUE(cmd_handler()->SendConnectionRequest(kPsm, kLocalCId, std::move(on_conn_rsp)));
    RETURN_IF_FATAL(RunLoopUntilIdle());
    EXPECT_EQ(1u, failed_requests());
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundInfoReqRspNotSupported) {
    BrEdrCommandHandler::InformationRequestCallback cb = [](InformationType type, auto responder) {
      EXPECT_EQ(InformationType::kConnectionlessMTU, type);
      responder->SendNotSupported();
    };
    cmd_handler()->ServeInformationRequest(std::move(cb));
  
    // Information Request payload
    auto info_req = CreateStaticByteBuffer(
        // Type = Connectionless MTU
        0x01, 0x00);
  
    // Information Response payload
    auto expected_rsp = CreateStaticByteBuffer(
        // Type = Connectionless MTU
        0x01, 0x00,
  
        // Result = Not supported
        0x01, 0x00);
  
    RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kInformationRequest, info_req, expected_rsp));
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundInfoReqRspConnlessMtu) {
    BrEdrCommandHandler::InformationRequestCallback cb = [](InformationType type, auto responder) {
      EXPECT_EQ(InformationType::kConnectionlessMTU, type);
      responder->SendConnectionlessMtu(0x02dc);
    };
    cmd_handler()->ServeInformationRequest(std::move(cb));
  
    // Information Request payload
    auto info_req = CreateStaticByteBuffer(
        // Type = Connectionless MTU
        0x01, 0x00);
  
    // Information Response payload
    auto expected_rsp = CreateStaticByteBuffer(
        // Type = Connectionless MTU
        0x01, 0x00,
  
        // Result = Success
        0x00, 0x00,
  
        // Data (MTU)
        0xdc, 0x02);
  
    RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kInformationRequest, info_req, expected_rsp));
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundInfoReqRspExtendedFeatures) {
    BrEdrCommandHandler::InformationRequestCallback cb = [](InformationType type, auto responder) {
      EXPECT_EQ(InformationType::kExtendedFeaturesSupported, type);
      responder->SendExtendedFeaturesSupported(0xfaceb00c);
    };
    cmd_handler()->ServeInformationRequest(std::move(cb));
  
    // Information Request payload
    auto info_req = CreateStaticByteBuffer(
        // Type = Features Mask
        0x02, 0x00);
  
    // Information Response payload
    auto expected_rsp = CreateStaticByteBuffer(
        // Type = Features Mask
        0x02, 0x00,
  
        // Result = Success
        0x00, 0x00,
  
        // Data (Mask)
        0x0c, 0xb0, 0xce, 0xfa);
  
    RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kInformationRequest, info_req, expected_rsp));
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundInfoReqRspFixedChannels) {
    BrEdrCommandHandler::InformationRequestCallback cb = [](InformationType type, auto responder) {
      EXPECT_EQ(InformationType::kFixedChannelsSupported, type);
      responder->SendFixedChannelsSupported(0xcafef00d4badc0deUL);
    };
    cmd_handler()->ServeInformationRequest(std::move(cb));
  
    // Information Request payload
    auto info_req = CreateStaticByteBuffer(
        // Type = Fixed Channels
        0x03, 0x00);
  
    // Configuration Response payload
    auto expected_rsp = CreateStaticByteBuffer(
        // Type = Fixed Channels
        0x03, 0x00,
  
        // Result = Success
        0x00, 0x00,
  
        // Data (Mask)
        0xde, 0xc0, 0xad, 0x4b, 0x0d, 0xf0, 0xfe, 0xca);
  
    RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kInformationRequest, info_req, expected_rsp));
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundConfigReqEmptyRspOkEmpty) {
    BrEdrCommandHandler::ConfigurationRequestCallback cb =
        [](ChannelId local_cid, uint16_t flags, ChannelConfiguration config, auto responder) {
          EXPECT_EQ(kLocalCId, local_cid);
          EXPECT_EQ(0x6006, flags);
          EXPECT_FALSE(config.mtu_option().has_value());
          EXPECT_FALSE(config.retransmission_flow_control_option().has_value());
          EXPECT_EQ(0u, config.unknown_options().size());
  
          responder->Send(kRemoteCId, 0x0001, ConfigurationResult::kPending,
                          ChannelConfiguration::ConfigurationOptions());
        };
    cmd_handler()->ServeConfigurationRequest(std::move(cb));
  
    // Configuration Request payload
    auto config_req = CreateStaticByteBuffer(
        // Destination Channel ID
        LowerBits(kLocalCId), UpperBits(kLocalCId),
  
        // Flags
        0x06, 0x60);
  
    // Configuration Response payload
    auto expected_rsp = CreateStaticByteBuffer(
        // Destination Channel ID
        LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
        // Flags
        0x01, 0x00,
  
        // Result = Pending
        0x04, 0x00);
  
    RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kConfigurationRequest, config_req, expected_rsp));
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConfigReqRspPendingEmpty) {
    // Configuration Request payload
    auto expected_config_req = CreateStaticByteBuffer(
        // Destination CID
        LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
        // Flags (non-zero to test encoding)
        0x01, 0x00,
  
        // Data (Config Options)
        0x01,       // Type = MTU
        0x02,       // Length = 2
        0x30, 0x00  // MTU = 48
    );
  
    // Configuration Response payload
    auto pending_config_req = CreateStaticByteBuffer(
        // Source CID
        LowerBits(kLocalCId), UpperBits(kLocalCId),
  
        // Flags (non-zero to test encoding)
        0x04, 0x00,
  
        // Result = Pending
        0x04, 0x00,
  
        // Data (Config Options)
        0x01,       // Type = MTU
        0x02,       // Length = 2
        0x60, 0x00  // MTU = 96
    );
  
    EXPECT_OUTBOUND_REQ(*fake_sig(), kConfigurationRequest, expected_config_req.view(),
                        {SignalingChannel::Status::kSuccess, pending_config_req.view()});
  
    bool cb_called = false;
    BrEdrCommandHandler::ConfigurationResponseCallback on_config_rsp =
        [&cb_called](const BrEdrCommandHandler::ConfigurationResponse& rsp) {
          cb_called = true;
          EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status());
          EXPECT_EQ(kLocalCId, rsp.local_cid());
          EXPECT_EQ(0x0004, rsp.flags());
          EXPECT_EQ(ConfigurationResult::kPending, rsp.result());
          EXPECT_TRUE(rsp.config().mtu_option().has_value());
          EXPECT_EQ(96u, rsp.config().mtu_option()->mtu());
          return SignalingChannel::ResponseHandlerAction::kCompleteOutboundTransaction;
        };
  
    ChannelConfiguration config;
    config.set_mtu_option(ChannelConfiguration::MtuOption(48));
  
    EXPECT_TRUE(cmd_handler()->SendConfigurationRequest(kRemoteCId, 0x0001, config.Options(),
                                                        std::move(on_config_rsp)));
    RunLoopUntilIdle();
    EXPECT_TRUE(cb_called);
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundConfigReqRspTimeOut) {
    // Configuration Request payload
    auto expected_config_req = CreateStaticByteBuffer(
        // Destination CID
        LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
        // Flags (non-zero to test encoding)
        0x01, 0xf0,
  
        // Data (Config Options)
        0x01,       // Type = MTU
        0x02,       // Length = 2
        0x30, 0x00  // MTU = 48
    );
  
    // Disconnect Request payload
    auto expected_discon_req = CreateStaticByteBuffer(
        // Destination CID
        LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
        // Source CID
        LowerBits(kLocalCId), UpperBits(kLocalCId));
  
    EXPECT_OUTBOUND_REQ(*fake_sig(), kConfigurationRequest, expected_config_req.view(),
                        {SignalingChannel::Status::kTimeOut, {}});
    EXPECT_OUTBOUND_REQ(*fake_sig(), kDisconnectionRequest, expected_discon_req.view());
  
    set_request_fail_callback([this]() {
      // Should still be allowed to send requests even after one failed
      auto on_discon_rsp = [](auto&) {};
      EXPECT_TRUE(
          cmd_handler()->SendDisconnectionRequest(kRemoteCId, kLocalCId, std::move(on_discon_rsp)));
    });
  
    ASSERT_EQ(0u, failed_requests());
  
    auto on_config_rsp = [](auto&) {
      ADD_FAILURE();
      return SignalingChannel::ResponseHandlerAction::kCompleteOutboundTransaction;
    };
  
    ChannelConfiguration config;
    config.set_mtu_option(ChannelConfiguration::MtuOption(48));
  
    EXPECT_TRUE(cmd_handler()->SendConfigurationRequest(kRemoteCId, 0xf001, config.Options(),
                                                        std::move(on_config_rsp)));
    RETURN_IF_FATAL(RunLoopUntilIdle());
    EXPECT_EQ(1u, failed_requests());
  }
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqRspOk) {
    // Information Request payload
    auto expected_info_req = CreateStaticByteBuffer(
        // Information Type
        LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
        UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)));
  
    // Information Response payload
    auto ok_info_rsp = CreateStaticByteBuffer(
        // Information Type
        LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
        UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
  
        // Information Result
        LowerBits(static_cast<uint16_t>(InformationResult::kSuccess)),
        UpperBits(static_cast<uint16_t>(InformationResult::kSuccess)),
  
        // Data (extended features mask, 4 bytes)
        LowerBits(kExtendedFeaturesBitFixedChannels), 0, 0, 0);
  
    EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
                        {SignalingChannel::Status::kSuccess, ok_info_rsp.view()});
  
    bool cb_called = false;
    BrEdrCommandHandler::InformationResponseCallback on_info_cb =
        [&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) {
          cb_called = true;
          EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status());
          EXPECT_EQ(InformationResult::kSuccess, rsp.result());
          EXPECT_EQ(InformationType::kExtendedFeaturesSupported, rsp.type());
          EXPECT_EQ(kExtendedFeaturesBitFixedChannels, rsp.extended_features());
        };
  
    EXPECT_TRUE(cmd_handler()->SendInformationRequest(InformationType::kExtendedFeaturesSupported,
                                                      std::move(on_info_cb)));
    RunLoopUntilIdle();
    EXPECT_TRUE(cb_called);
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqRspNotSupported) {
    // Information Request payload
    auto expected_info_req = CreateStaticByteBuffer(
        // Information Type
        LowerBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)),
        UpperBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)));
  
    // Information Response payload
    auto error_info_rsp = CreateStaticByteBuffer(
        // Information Type
        LowerBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)),
        UpperBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)),
  
        // Information Result
        LowerBits(static_cast<uint16_t>(InformationResult::kNotSupported)),
        UpperBits(static_cast<uint16_t>(InformationResult::kNotSupported)));
  
    EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
                        {SignalingChannel::Status::kSuccess, error_info_rsp.view()});
  
    bool cb_called = false;
    BrEdrCommandHandler::InformationResponseCallback on_info_cb =
        [&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) {
          cb_called = true;
          EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status());
          EXPECT_EQ(InformationResult::kNotSupported, rsp.result());
          EXPECT_EQ(InformationType::kConnectionlessMTU, rsp.type());
        };
  
    EXPECT_TRUE(cmd_handler()->SendInformationRequest(InformationType::kConnectionlessMTU,
                                                      std::move(on_info_cb)));
    RunLoopUntilIdle();
    EXPECT_TRUE(cb_called);
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqRspHeaderNotEnoughBytes) {
    // Information Request payload
    auto expected_info_req = CreateStaticByteBuffer(
        // Information Type
        LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
        UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)));
  
    // Information Response payload
    auto malformed_info_rsp = CreateStaticByteBuffer(
        // 1 of 4 bytes expected of an Information Response just to be able to parse it.
        LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)));
  
    EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
                        {SignalingChannel::Status::kSuccess, malformed_info_rsp.view()});
  
    bool cb_called = false;
    BrEdrCommandHandler::InformationResponseCallback on_info_cb =
        [&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) { cb_called = true; };
  
    EXPECT_TRUE(cmd_handler()->SendInformationRequest(InformationType::kExtendedFeaturesSupported,
                                                      std::move(on_info_cb)));
    RunLoopUntilIdle();
    EXPECT_FALSE(cb_called);
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqRspPayloadNotEnoughBytes) {
    // Information Request payload
    auto expected_info_req = CreateStaticByteBuffer(
        // Information Type
        LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
        UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)));
  
    // Information Response payload
    auto malformed_info_rsp = CreateStaticByteBuffer(
        // Information Type
        LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
        UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
  
        // Information Result
        LowerBits(static_cast<uint16_t>(InformationResult::kSuccess)),
        UpperBits(static_cast<uint16_t>(InformationResult::kSuccess)),
  
        // Data (2 of 4 bytes expected of an extended features mask)
        0, 0);
  
    EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
                        {SignalingChannel::Status::kSuccess, malformed_info_rsp.view()});
  
    bool cb_called = false;
    BrEdrCommandHandler::InformationResponseCallback on_info_cb =
        [&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) { cb_called = true; };
  
    EXPECT_TRUE(cmd_handler()->SendInformationRequest(InformationType::kExtendedFeaturesSupported,
                                                      std::move(on_info_cb)));
    RunLoopUntilIdle();
    EXPECT_FALSE(cb_called);
  }
  
  // Accept and pass a valid Information Response even if it doesn't have the type
  // requested.
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqRspWrongType) {
    // Information Request payload
    auto expected_info_req = CreateStaticByteBuffer(
        // Information Type
        LowerBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)),
        UpperBits(static_cast<uint16_t>(InformationType::kExtendedFeaturesSupported)));
  
    // Information Response payload
    auto mismatch_info_rsp = CreateStaticByteBuffer(
        // Information Type
        LowerBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)),
        UpperBits(static_cast<uint16_t>(InformationType::kConnectionlessMTU)),
  
        // Information Result
        LowerBits(static_cast<uint16_t>(InformationResult::kSuccess)),
        UpperBits(static_cast<uint16_t>(InformationResult::kSuccess)),
  
        // Data (connectionless broadcast MTU, 2 bytes)
        0x40, 0);
  
    EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
                        {SignalingChannel::Status::kSuccess, mismatch_info_rsp.view()});
  
    bool cb_called = false;
    BrEdrCommandHandler::InformationResponseCallback on_info_cb =
        [&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) {
          cb_called = true;
          EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status());
          EXPECT_EQ(InformationResult::kSuccess, rsp.result());
          EXPECT_EQ(InformationType::kConnectionlessMTU, rsp.type());
          EXPECT_EQ(0x40, rsp.connectionless_mtu());
        };
  
    EXPECT_TRUE(cmd_handler()->SendInformationRequest(InformationType::kExtendedFeaturesSupported,
                                                      std::move(on_info_cb)));
    RunLoopUntilIdle();
    EXPECT_TRUE(cb_called);
  }
  
  // Allow types of information besides those known to BrEdrCommandHandler.
  TEST_F(L2CAP_BrEdrCommandHandlerTest, OutboundInfoReqUnknownType) {
    // Information Request payload
    auto expected_info_req = CreateStaticByteBuffer(
        // Information Type
        0x04, 0);
  
    // Information Response payload
    auto ok_info_rsp = CreateStaticByteBuffer(
        // Information Type
        0x04, 0,
  
        // Information Result
        LowerBits(static_cast<uint16_t>(InformationResult::kSuccess)),
        UpperBits(static_cast<uint16_t>(InformationResult::kSuccess)),
  
        // Data (some payload)
        't', 'e', 's', 't');
  
    EXPECT_OUTBOUND_REQ(*fake_sig(), kInformationRequest, expected_info_req.view(),
                        {SignalingChannel::Status::kSuccess, ok_info_rsp.view()});
  
    bool cb_called = false;
    BrEdrCommandHandler::InformationResponseCallback on_info_cb =
        [&cb_called](const BrEdrCommandHandler::InformationResponse& rsp) {
          cb_called = true;
          EXPECT_EQ(SignalingChannel::Status::kSuccess, rsp.status());
          EXPECT_EQ(InformationResult::kSuccess, rsp.result());
          EXPECT_EQ(static_cast<InformationType>(0x04), rsp.type());
        };
  
    EXPECT_TRUE(cmd_handler()->SendInformationRequest(static_cast<InformationType>(0x04),
                                                      std::move(on_info_cb)));
    RunLoopUntilIdle();
    EXPECT_TRUE(cb_called);
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundConnReqRspPending) {
    BrEdrCommandHandler::ConnectionRequestCallback cb = [](PSM psm, ChannelId remote_cid,
                                                           auto responder) {
      EXPECT_EQ(kPsm, psm);
      EXPECT_EQ(kRemoteCId, remote_cid);
      responder->Send(kLocalCId, ConnectionResult::kPending, ConnectionStatus::kAuthorizationPending);
    };
    cmd_handler()->ServeConnectionRequest(std::move(cb));
  
    // Connection Request payload
    auto conn_req = CreateStaticByteBuffer(
        // PSM
        LowerBits(kPsm), UpperBits(kPsm),
  
        // Source CID (relative to requester)
        LowerBits(kRemoteCId), UpperBits(kRemoteCId));
  
    // Connection Response payload
    auto conn_rsp = CreateStaticByteBuffer(
        // Destination CID (relative to requester)
        LowerBits(kLocalCId), UpperBits(kLocalCId),
  
        // Source CID (relative to requester)
        LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
        // Connection Result
        LowerBits(static_cast<uint16_t>(ConnectionResult::kPending)),
        UpperBits(static_cast<uint16_t>(ConnectionResult::kPending)),
  
        // Connection Status
        LowerBits(static_cast<uint16_t>(ConnectionStatus::kAuthorizationPending)),
        UpperBits(static_cast<uint16_t>(ConnectionStatus::kAuthorizationPending)));
  
    RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kConnectionRequest, conn_req, conn_rsp));
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundConnReqBadPsm) {
    constexpr uint16_t kBadPsm = 0x0002;
  
    // Request callback shouldn't even be called for an invalid PSM.
    bool req_cb_called = false;
    BrEdrCommandHandler::ConnectionRequestCallback cb =
        [&req_cb_called](PSM psm, ChannelId remote_cid, auto responder) { req_cb_called = true; };
    cmd_handler()->ServeConnectionRequest(std::move(cb));
  
    // Connection Request payload
    auto conn_req = CreateStaticByteBuffer(
        // PSM
        LowerBits(kBadPsm), UpperBits(kBadPsm),
  
        // Source CID (relative to requester)
        LowerBits(kRemoteCId), UpperBits(kRemoteCId));
  
    // Connection Response payload
    auto conn_rsp = CreateStaticByteBuffer(
        // Destination CID (relative to requester)
        LowerBits(kInvalidChannelId), UpperBits(kInvalidChannelId),
  
        // Source CID (relative to requester)
        LowerBits(kRemoteCId), UpperBits(kRemoteCId),
  
        // Connection Result
        LowerBits(static_cast<uint16_t>(ConnectionResult::kPSMNotSupported)),
        UpperBits(static_cast<uint16_t>(ConnectionResult::kPSMNotSupported)),
  
        // Connection Status
        LowerBits(static_cast<uint16_t>(ConnectionStatus::kNoInfoAvailable)),
        UpperBits(static_cast<uint16_t>(ConnectionStatus::kNoInfoAvailable)));
  
    RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kConnectionRequest, conn_req, conn_rsp));
    EXPECT_FALSE(req_cb_called);
  }
  
  TEST_F(L2CAP_BrEdrCommandHandlerTest, InboundConnReqNonDynamicSrcCId) {
    // Request callback shouldn't even be called for an invalid Source Channel ID.
    bool req_cb_called = false;
    BrEdrCommandHandler::ConnectionRequestCallback cb =
        [&req_cb_called](PSM psm, ChannelId remote_cid, auto responder) { req_cb_called = true; };
    cmd_handler()->ServeConnectionRequest(std::move(cb));
  
    // Connection Request payload
    auto conn_req = CreateStaticByteBuffer(
        // PSM
        LowerBits(kPsm), UpperBits(kPsm),
  
        // Source CID: fixed channel for Security Manager (relative to requester)
        LowerBits(kSMPChannelId), UpperBits(kSMPChannelId));
  
    // Connection Response payload
    auto conn_rsp = CreateStaticByteBuffer(
        // Destination CID (relative to requester)
        LowerBits(kInvalidChannelId), UpperBits(kInvalidChannelId),
  
        // Source CID (relative to requester)
        LowerBits(kSMPChannelId), UpperBits(kSMPChannelId),
  
        // Connection Result
        LowerBits(static_cast<uint16_t>(ConnectionResult::kInvalidSourceCID)),
        UpperBits(static_cast<uint16_t>(ConnectionResult::kInvalidSourceCID)),
  
        // Connection Status
        LowerBits(static_cast<uint16_t>(ConnectionStatus::kNoInfoAvailable)),
        UpperBits(static_cast<uint16_t>(ConnectionStatus::kNoInfoAvailable)));
  
    RETURN_IF_FATAL(fake_sig()->ReceiveExpect(kConnectionRequest, conn_req, conn_rsp));
    EXPECT_FALSE(req_cb_called);
  }
  
  }  // namespace
  }  // namespace internal
  }  // namespace l2cap
  }  // namespace bt
  