/******************************************************************************
*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
#include <base/logging.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "bt_types.h"
#include "btm_api.h"
#include "l2c_api.h"
#include "osi/include/osi.h"
#include "port_api.h"
#include "btm_int.h"
#include "rfc_int.h"
#include "mock_btm_layer.h"
#include "mock_l2cap_layer.h"
#include "stack_rfcomm_test_utils.h"
#include "stack_test_packet_utils.h"
std::string DumpByteBufferToString(uint8_t* p_data, size_t len) {
std::stringstream str;
str.setf(std::ios_base::hex, std::ios::basefield);
str.setf(std::ios_base::uppercase);
str.fill('0');
for (size_t i = 0; i < len; ++i) {
str << std::setw(2) << static_cast<uint16_t>(p_data[i]);
str << " ";
}
return str.str();
}
std::string DumpBtHdrToString(BT_HDR* p_hdr) {
uint8_t* p_hdr_data = p_hdr->data + p_hdr->offset;
return DumpByteBufferToString(p_hdr_data, p_hdr->len);
}
void PrintTo(BT_HDR* value, ::std::ostream* os) {
*os << DumpBtHdrToString(value);
}
void PrintTo(tL2CAP_CFG_INFO* value, ::std::ostream* os) {
*os << DumpByteBufferToString((uint8_t*)value, sizeof(tL2CAP_CFG_INFO));
}
namespace {
using testing::_;
using testing::DoAll;
using testing::Return;
using testing::Test;
using testing::StrictMock;
using testing::SaveArg;
using testing::SaveArgPointee;
using testing::Pointee;
using testing::StrEq;
using testing::NotNull;
using bluetooth::CreateL2capDataPacket;
using bluetooth::CreateAclPacket;
using bluetooth::AllocateWrappedIncomingL2capAclPacket;
using bluetooth::AllocateWrappedOutgoingL2capAclPacket;
using bluetooth::rfcomm::GetDlci;
using bluetooth::rfcomm::GetAddressField;
using bluetooth::rfcomm::GetControlField;
using bluetooth::rfcomm::CreateMccPnFrame;
using bluetooth::rfcomm::CreateMccMscFrame;
using bluetooth::rfcomm::CreateMultiplexerControlFrame;
using bluetooth::rfcomm::CreateRfcommPacket;
using bluetooth::rfcomm::CreateQuickDataPacket;
using bluetooth::rfcomm::CreateQuickPnPacket;
using bluetooth::rfcomm::CreateQuickSabmPacket;
using bluetooth::rfcomm::CreateQuickUaPacket;
using bluetooth::rfcomm::CreateQuickMscPacket;
MATCHER_P(PointerMemoryEqual, ptr,
DumpByteBufferToString((uint8_t*)ptr, sizeof(*ptr))) {
return memcmp(arg, ptr, sizeof(*ptr)) == 0;
}
MATCHER_P(BtHdrEqual, expected, DumpBtHdrToString(expected)) {
auto arg_hdr = static_cast<BT_HDR*>(arg);
uint8_t* arg_data = arg_hdr->data + arg_hdr->offset;
auto expected_hdr = static_cast<BT_HDR*>(expected);
uint8_t* expected_data = expected_hdr->data + expected_hdr->offset;
return memcmp(arg_data, expected_data, expected_hdr->len) == 0;
}
bluetooth::rfcomm::MockRfcommCallback* rfcomm_callback = nullptr;
void port_mgmt_cback_0(uint32_t code, uint16_t port_handle) {
rfcomm_callback->PortManagementCallback(code, port_handle, 0);
}
void port_mgmt_cback_1(uint32_t code, uint16_t port_handle) {
rfcomm_callback->PortManagementCallback(code, port_handle, 1);
}
void port_event_cback_0(uint32_t code, uint16_t port_handle) {
rfcomm_callback->PortEventCallback(code, port_handle, 0);
}
void port_event_cback_1(uint32_t code, uint16_t port_handle) {
rfcomm_callback->PortEventCallback(code, port_handle, 1);
}
RawAddress GetTestAddress(int index) {
CHECK_LT(index, UINT8_MAX);
RawAddress result = {
{0xAA, 0x00, 0x11, 0x22, 0x33, static_cast<uint8_t>(index)}};
return result;
}
class StackRfcommTest : public Test {
public:
void StartServerPort(uint16_t uuid, uint8_t scn, uint16_t mtu,
tPORT_CALLBACK* management_callback,
tPORT_CALLBACK* event_callback,
uint16_t* server_handle) {
VLOG(1) << "Step 1";
ASSERT_EQ(RFCOMM_CreateConnection(uuid, scn, true, mtu, RawAddress::kAny,
server_handle, management_callback),
PORT_SUCCESS);
ASSERT_EQ(PORT_SetEventMask(*server_handle, PORT_EV_RXCHAR), PORT_SUCCESS);
ASSERT_EQ(PORT_SetEventCallback(*server_handle, event_callback),
PORT_SUCCESS);
}
void ConnectServerL2cap(const RawAddress& peer_addr, uint16_t acl_handle,
uint16_t lcid) {
VLOG(1) << "Step 1";
// Remote device connect to this channel, we shall accept
static const uint8_t cmd_id = 0x07;
EXPECT_CALL(l2cap_interface_,
ConnectResponse(peer_addr, cmd_id, lcid, L2CAP_CONN_OK, 0));
tL2CAP_CFG_INFO cfg_req = {.mtu_present = true, .mtu = L2CAP_MTU_SIZE};
EXPECT_CALL(l2cap_interface_,
ConfigRequest(lcid, PointerMemoryEqual(&cfg_req)))
.WillOnce(Return(true));
l2cap_appl_info_.pL2CA_ConnectInd_Cb(peer_addr, lcid, BT_PSM_RFCOMM,
cmd_id);
VLOG(1) << "Step 2";
// MTU configuration is done
cfg_req.mtu_present = false;
l2cap_appl_info_.pL2CA_ConfigCfm_Cb(lcid, &cfg_req);
VLOG(1) << "Step 3";
// Remote device also ask to configure MTU size
EXPECT_CALL(l2cap_interface_,
ConfigResponse(lcid, PointerMemoryEqual(&cfg_req)))
.WillOnce(Return(true));
l2cap_appl_info_.pL2CA_ConfigInd_Cb(lcid, &cfg_req);
VLOG(1) << "Step 4";
// Remote device connect to server channel 0
BT_HDR* sabm_channel_0 = AllocateWrappedIncomingL2capAclPacket(
CreateQuickSabmPacket(RFCOMM_MX_DLCI, lcid, acl_handle));
BT_HDR* ua_channel_0 = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickUaPacket(RFCOMM_MX_DLCI, lcid, acl_handle));
EXPECT_CALL(l2cap_interface_, DataWrite(lcid, BtHdrEqual(ua_channel_0)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
// Packet should be freed by RFCOMM
l2cap_appl_info_.pL2CA_DataInd_Cb(lcid, sabm_channel_0);
osi_free(ua_channel_0);
}
void ConnectServerPort(const RawAddress& peer_addr, uint16_t port_handle,
uint8_t scn, uint16_t mtu, uint16_t acl_handle,
uint16_t lcid, int port_callback_index) {
VLOG(1) << "Step 1";
// Negotiate parameters on scn
BT_HDR* uih_pn_cmd_from_peer = AllocateWrappedIncomingL2capAclPacket(
CreateQuickPnPacket(true, GetDlci(false, scn), true, mtu,
RFCOMM_PN_CONV_LAYER_CBFC_I >> 4, 0, RFCOMM_K_MAX,
lcid, acl_handle));
BT_HDR* uih_pn_rsp_to_peer = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickPnPacket(false, GetDlci(false, scn), false, mtu,
RFCOMM_PN_CONV_LAYER_CBFC_R >> 4, 0, RFCOMM_K_MAX,
lcid, acl_handle));
EXPECT_CALL(l2cap_interface_,
DataWrite(lcid, BtHdrEqual(uih_pn_rsp_to_peer)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
// uih_pn_cmd_from_peer should be freed by this method
l2cap_appl_info_.pL2CA_DataInd_Cb(lcid, uih_pn_cmd_from_peer);
osi_free(uih_pn_rsp_to_peer);
VLOG(1) << "Step 2";
// Remote device connect to scn
tBTM_SEC_CALLBACK* security_callback = nullptr;
void* p_port = nullptr;
BT_HDR* sabm_channel_dlci = AllocateWrappedIncomingL2capAclPacket(
CreateQuickSabmPacket(GetDlci(false, scn), lcid, acl_handle));
EXPECT_CALL(btm_security_internal_interface_,
MultiplexingProtocolAccessRequest(peer_addr, BT_PSM_RFCOMM,
false, BTM_SEC_PROTO_RFCOMM,
scn, NotNull(), NotNull()))
.WillOnce(DoAll(SaveArg<5>(&security_callback), SaveArg<6>(&p_port),
Return(BTM_SUCCESS)));
// sabm_channel_dlci should be freed by this method
l2cap_appl_info_.pL2CA_DataInd_Cb(lcid, sabm_channel_dlci);
VLOG(1) << "Step 3";
// Confirm security check should trigger port as connected
EXPECT_CALL(
rfcomm_callback_,
PortManagementCallback(PORT_SUCCESS, port_handle, port_callback_index));
BT_HDR* ua_channel_dlci = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickUaPacket(GetDlci(false, scn), lcid, acl_handle));
EXPECT_CALL(l2cap_interface_, DataWrite(lcid, BtHdrEqual(ua_channel_dlci)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
ASSERT_TRUE(security_callback);
security_callback(&peer_addr, BT_TRANSPORT_BR_EDR, p_port, BTM_SUCCESS);
osi_free(ua_channel_dlci);
VLOG(1) << "Step 4";
// Remote also need to configure its modem signal before we can send data
BT_HDR* uih_msc_cmd_from_peer = AllocateWrappedIncomingL2capAclPacket(
CreateQuickMscPacket(true, GetDlci(false, scn), lcid, acl_handle, true,
false, true, true, false, true));
BT_HDR* uih_msc_response_to_peer = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickMscPacket(false, GetDlci(false, scn), lcid, acl_handle,
false, false, true, true, false, true));
// We also have to do modem configuration ourself
EXPECT_CALL(l2cap_interface_,
DataWrite(lcid, BtHdrEqual(uih_msc_response_to_peer)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
BT_HDR* uih_msc_cmd_to_peer = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickMscPacket(false, GetDlci(false, scn), lcid, acl_handle, true,
false, true, true, false, true));
EXPECT_CALL(l2cap_interface_,
DataWrite(lcid, BtHdrEqual(uih_msc_cmd_to_peer)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
// uih_msc_cmd_from_peer should be freed by this method
l2cap_appl_info_.pL2CA_DataInd_Cb(lcid, uih_msc_cmd_from_peer);
osi_free(uih_msc_response_to_peer);
VLOG(1) << "Step 5";
// modem configuration is done
BT_HDR* uih_msc_response_from_peer = AllocateWrappedIncomingL2capAclPacket(
CreateQuickMscPacket(true, GetDlci(false, scn), lcid, acl_handle, false,
false, true, true, false, true));
// uih_msc_response_from_peer should be freed by this method
l2cap_appl_info_.pL2CA_DataInd_Cb(lcid, uih_msc_response_from_peer);
}
void StartClientPort(const RawAddress& peer_bd_addr, uint16_t uuid,
uint8_t scn, uint16_t mtu,
tPORT_CALLBACK* management_callback,
tPORT_CALLBACK* event_callback, uint16_t lcid,
uint16_t acl_handle, uint16_t* client_handle,
bool is_first_connection) {
VLOG(1) << "Step 1";
BT_HDR* uih_pn_channel_3 =
AllocateWrappedOutgoingL2capAclPacket(CreateQuickPnPacket(
true, GetDlci(false, scn), true, mtu, RFCOMM_PN_CONV_LAYER_TYPE_1,
RFCOMM_PN_PRIORITY_0, RFCOMM_K, lcid, acl_handle));
if (is_first_connection) {
EXPECT_CALL(l2cap_interface_, ConnectRequest(BT_PSM_RFCOMM, peer_bd_addr))
.WillOnce(Return(lcid));
} else {
EXPECT_CALL(l2cap_interface_,
DataWrite(lcid, BtHdrEqual(uih_pn_channel_3)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
}
ASSERT_EQ(RFCOMM_CreateConnection(uuid, scn, false, mtu, peer_bd_addr,
client_handle, management_callback),
PORT_SUCCESS);
ASSERT_EQ(PORT_SetEventMask(*client_handle, PORT_EV_RXCHAR), PORT_SUCCESS);
ASSERT_EQ(PORT_SetEventCallback(*client_handle, event_callback),
PORT_SUCCESS);
osi_free(uih_pn_channel_3);
}
void TestConnectClientPortL2cap(uint16_t acl_handle, uint16_t lcid) {
VLOG(1) << "Step 1";
// Send configuration request when L2CAP connect is succsseful
tL2CAP_CFG_INFO cfg_req = {.mtu_present = true, .mtu = L2CAP_MTU_SIZE};
EXPECT_CALL(l2cap_interface_,
ConfigRequest(lcid, PointerMemoryEqual(&cfg_req)))
.WillOnce(Return(true));
l2cap_appl_info_.pL2CA_ConnectCfm_Cb(lcid, L2CAP_CONN_OK);
VLOG(1) << "Step 2";
// Remote device confirms our configuration request
cfg_req.mtu_present = false;
l2cap_appl_info_.pL2CA_ConfigCfm_Cb(lcid, &cfg_req);
VLOG(1) << "Step 3";
// Remote device also asks to configure MTU
// Once configuration is done, we connect to multiplexer control channel 0
EXPECT_CALL(l2cap_interface_,
ConfigResponse(lcid, PointerMemoryEqual(&cfg_req)))
.WillOnce(Return(true));
// multiplexer control channel's DLCI is always 0
BT_HDR* sabm_channel_0 = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickSabmPacket(RFCOMM_MX_DLCI, lcid, acl_handle));
EXPECT_CALL(l2cap_interface_, DataWrite(lcid, BtHdrEqual(sabm_channel_0)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
l2cap_appl_info_.pL2CA_ConfigInd_Cb(lcid, &cfg_req);
osi_free(sabm_channel_0);
}
void ConnectClientPort(const RawAddress& peer_addr, uint16_t port_handle,
uint8_t scn, uint16_t mtu, uint16_t acl_handle,
uint16_t lcid, int port_callback_index,
bool is_first_connection) {
VLOG(1) << "Step 1";
if (is_first_connection) {
VLOG(1) << "Step 1.5";
// Once remote accept multiplexer control channel 0
// We change to desired channel on non-initiating device (remote device)
BT_HDR* ua_channel_0 = AllocateWrappedIncomingL2capAclPacket(
CreateQuickUaPacket(RFCOMM_MX_DLCI, lcid, acl_handle));
BT_HDR* uih_pn_channel_3 =
AllocateWrappedOutgoingL2capAclPacket(CreateQuickPnPacket(
true, GetDlci(false, scn), true, mtu,
RFCOMM_PN_CONV_LAYER_CBFC_I >> 4, RFCOMM_PN_PRIORITY_0,
RFCOMM_K_MAX, lcid, acl_handle));
EXPECT_CALL(l2cap_interface_,
DataWrite(lcid, BtHdrEqual(uih_pn_channel_3)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
l2cap_appl_info_.pL2CA_DataInd_Cb(lcid, ua_channel_0);
osi_free(uih_pn_channel_3);
}
VLOG(1) << "Step 2";
// Once remote accept service channel change, we start security procedure
BT_HDR* uih_pn_channel_3_accept =
AllocateWrappedIncomingL2capAclPacket(CreateQuickPnPacket(
false, GetDlci(false, scn), false, mtu,
RFCOMM_PN_CONV_LAYER_CBFC_I >> 4, RFCOMM_PN_PRIORITY_0,
RFCOMM_K_MAX, lcid, acl_handle));
tBTM_SEC_CALLBACK* security_callback = nullptr;
void* p_port = nullptr;
EXPECT_CALL(btm_security_internal_interface_,
MultiplexingProtocolAccessRequest(peer_addr, BT_PSM_RFCOMM,
true, BTM_SEC_PROTO_RFCOMM,
scn, NotNull(), NotNull()))
.WillOnce(DoAll(SaveArg<5>(&security_callback), SaveArg<6>(&p_port),
Return(BTM_SUCCESS)));
l2cap_appl_info_.pL2CA_DataInd_Cb(lcid, uih_pn_channel_3_accept);
VLOG(1) << "Step 3";
// Once security procedure is done, we officially connect to target scn
BT_HDR* sabm_channel_3 = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickSabmPacket(GetDlci(false, scn), lcid, acl_handle));
EXPECT_CALL(l2cap_interface_, DataWrite(lcid, BtHdrEqual(sabm_channel_3)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
ASSERT_TRUE(security_callback);
security_callback(&peer_addr, BT_TRANSPORT_BR_EDR, p_port, BTM_SUCCESS);
osi_free(sabm_channel_3);
VLOG(1) << "Step 4";
// When target scn is accepted by remote, we need to configure modem signal
// state beofre using the port
EXPECT_CALL(
rfcomm_callback_,
PortManagementCallback(PORT_SUCCESS, port_handle, port_callback_index));
BT_HDR* uih_msc_cmd = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickMscPacket(true, GetDlci(false, scn), lcid, acl_handle, true,
false, true, true, false, true));
EXPECT_CALL(l2cap_interface_, DataWrite(lcid, BtHdrEqual(uih_msc_cmd)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
BT_HDR* ua_channel_3 = AllocateWrappedIncomingL2capAclPacket(
CreateQuickUaPacket(GetDlci(false, scn), lcid, acl_handle));
l2cap_appl_info_.pL2CA_DataInd_Cb(lcid, ua_channel_3);
osi_free(uih_msc_cmd);
VLOG(1) << "Step 5";
// modem configuration is done
BT_HDR* uih_msc_response = AllocateWrappedIncomingL2capAclPacket(
CreateQuickMscPacket(false, GetDlci(false, scn), lcid, acl_handle,
false, false, true, true, false, true));
l2cap_appl_info_.pL2CA_DataInd_Cb(lcid, uih_msc_response);
VLOG(1) << "Step 6";
// Remote also need to configure its modem signal before we can send data
BT_HDR* uih_msc_cmd_from_peer = AllocateWrappedIncomingL2capAclPacket(
CreateQuickMscPacket(false, GetDlci(false, scn), lcid, acl_handle, true,
false, true, true, false, true));
BT_HDR* uih_msc_response_to_peer = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickMscPacket(true, GetDlci(false, scn), lcid, acl_handle, false,
false, true, true, false, true));
EXPECT_CALL(l2cap_interface_,
DataWrite(lcid, BtHdrEqual(uih_msc_response_to_peer)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
l2cap_appl_info_.pL2CA_DataInd_Cb(lcid, uih_msc_cmd_from_peer);
osi_free(uih_msc_response_to_peer);
}
void SendAndVerifyOutgoingTransmission(uint16_t port_handle,
bool is_initiator, uint8_t scn,
bool cr, const std::string& message,
int credits, uint16_t acl_handle,
uint16_t lcid) {
VLOG(1) << "Step 1";
BT_HDR* data_packet = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickDataPacket(GetDlci(is_initiator, scn), cr, lcid, acl_handle,
credits, message));
uint16_t transmitted_length = 0;
EXPECT_CALL(l2cap_interface_, DataWrite(lcid, BtHdrEqual(data_packet)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
ASSERT_EQ(PORT_WriteData(port_handle, message.data(), message.size(),
&transmitted_length),
PORT_SUCCESS);
ASSERT_EQ(transmitted_length, message.size());
}
void ReceiveAndVerifyIncomingTransmission(uint16_t port_handle,
bool is_initiator, uint8_t scn,
bool cr, const std::string& message,
int credits, uint16_t acl_handle,
uint16_t lcid,
int port_callback_index) {
VLOG(1) << "Step 1";
BT_HDR* data_packet = AllocateWrappedIncomingL2capAclPacket(
CreateQuickDataPacket(GetDlci(is_initiator, scn), cr, lcid, acl_handle,
credits, message));
EXPECT_CALL(rfcomm_callback_,
PortEventCallback(_, port_handle, port_callback_index));
l2cap_appl_info_.pL2CA_DataInd_Cb(lcid, data_packet);
VLOG(1) << "Step 2";
char buffer[L2CAP_MTU_SIZE] = {};
uint16_t length = 0;
int status = PORT_ReadData(port_handle, buffer, L2CAP_MTU_SIZE, &length);
ASSERT_EQ(status, PORT_SUCCESS);
ASSERT_THAT(buffer, StrEq(message));
}
protected:
void SetUp() override {
Test::SetUp();
bluetooth::manager::SetMockSecurityInternalInterface(
&btm_security_internal_interface_);
bluetooth::l2cap::SetMockInterface(&l2cap_interface_);
rfcomm_callback = &rfcomm_callback_;
EXPECT_CALL(l2cap_interface_, Register(BT_PSM_RFCOMM, _, _))
.WillOnce(
DoAll(SaveArgPointee<1>(&l2cap_appl_info_), Return(BT_PSM_RFCOMM)));
RFCOMM_Init();
rfc_cb.trace_level = BT_TRACE_LEVEL_DEBUG;
}
void TearDown() override {
rfcomm_callback = nullptr;
bluetooth::l2cap::SetMockInterface(nullptr);
bluetooth::manager::SetMockSecurityInternalInterface(nullptr);
testing::Test::TearDown();
}
StrictMock<bluetooth::manager::MockBtmSecurityInternalInterface>
btm_security_internal_interface_;
StrictMock<bluetooth::l2cap::MockL2capInterface> l2cap_interface_;
StrictMock<bluetooth::rfcomm::MockRfcommCallback> rfcomm_callback_;
tL2CAP_APPL_INFO l2cap_appl_info_;
};
TEST_F(StackRfcommTest, SingleServerConnectionHelloWorld) {
// Prepare a server channel at kTestChannelNumber0
static const uint16_t acl_handle = 0x0009;
static const uint16_t lcid = 0x0054;
static const uint16_t test_uuid = 0x1112;
static const uint8_t test_scn = 8;
static const uint16_t test_mtu = 1600;
static const RawAddress test_address = GetTestAddress(0);
uint16_t server_handle = 0;
ASSERT_NO_FATAL_FAILURE(StartServerPort(test_uuid, test_scn, test_mtu,
port_mgmt_cback_0, port_event_cback_0,
&server_handle));
ASSERT_NO_FATAL_FAILURE(ConnectServerL2cap(test_address, acl_handle, lcid));
ASSERT_NO_FATAL_FAILURE(ConnectServerPort(
test_address, server_handle, test_scn, test_mtu, acl_handle, lcid, 0));
ASSERT_NO_FATAL_FAILURE(ReceiveAndVerifyIncomingTransmission(
server_handle, false, test_scn, true, "Hello World!\r", 50, acl_handle,
lcid, 0));
ASSERT_NO_FATAL_FAILURE(
SendAndVerifyOutgoingTransmission(server_handle, false, test_scn, false,
"\r!dlroW olleH", 4, acl_handle, lcid));
}
TEST_F(StackRfcommTest, MultiServerPortSameDeviceHelloWorld) {
// Prepare a server channel at kTestChannelNumber0
static const uint16_t acl_handle = 0x0009;
static const uint16_t lcid = 0x0054;
static const uint16_t test_mtu = 1600;
static const RawAddress test_address = GetTestAddress(0);
// Service 0
uint16_t server_handle_0 = 0;
static const uint8_t test_scn_0 = 8;
static const uint16_t test_uuid_0 = 0x1112;
ASSERT_NO_FATAL_FAILURE(StartServerPort(test_uuid_0, test_scn_0, test_mtu,
port_mgmt_cback_0, port_event_cback_0,
&server_handle_0));
ASSERT_NO_FATAL_FAILURE(ConnectServerL2cap(test_address, acl_handle, lcid));
ASSERT_NO_FATAL_FAILURE(ConnectServerPort(test_address, server_handle_0,
test_scn_0, test_mtu, acl_handle,
lcid, 0));
// Service 1
uint16_t server_handle_1 = 0;
static const uint8_t test_scn_1 = 10;
static const uint16_t test_uuid_1 = 0x111F;
ASSERT_NE(test_scn_1, test_scn_0);
ASSERT_NE(test_uuid_1, test_uuid_0);
ASSERT_NO_FATAL_FAILURE(StartServerPort(test_uuid_1, test_scn_1, test_mtu,
port_mgmt_cback_1, port_event_cback_1,
&server_handle_1));
// No L2CAP setup for 2nd device
ASSERT_NO_FATAL_FAILURE(ConnectServerPort(test_address, server_handle_1,
test_scn_1, test_mtu, acl_handle,
lcid, 1));
// Use service 0
ASSERT_NO_FATAL_FAILURE(ReceiveAndVerifyIncomingTransmission(
server_handle_0, false, test_scn_0, true, "Hello World0!\r", 50,
acl_handle, lcid, 0));
ASSERT_NO_FATAL_FAILURE(SendAndVerifyOutgoingTransmission(
server_handle_0, false, test_scn_0, false, "\r!0dlroW olleH", 4,
acl_handle, lcid));
// Use service 1
ASSERT_NO_FATAL_FAILURE(ReceiveAndVerifyIncomingTransmission(
server_handle_1, false, test_scn_1, true, "Hello World1!\r", 50,
acl_handle, lcid, 1));
ASSERT_NO_FATAL_FAILURE(SendAndVerifyOutgoingTransmission(
server_handle_1, false, test_scn_1, false, "\r!1dlroW olleH", 4,
acl_handle, lcid));
}
TEST_F(StackRfcommTest, SameServerPortMultiDeviceHelloWorld) {
// Prepare a server channel at kTestChannelNumber0
static const uint16_t test_mtu = 1600;
static const uint8_t test_scn = 3;
static const uint16_t test_uuid = 0x1112;
// Service 0
static const RawAddress test_address_0 = GetTestAddress(0);
static const uint16_t acl_handle_0 = 0x0009;
static const uint16_t lcid_0 = 0x0054;
uint16_t server_handle_0 = 0;
ASSERT_NO_FATAL_FAILURE(StartServerPort(test_uuid, test_scn, test_mtu,
port_mgmt_cback_0, port_event_cback_0,
&server_handle_0));
ASSERT_NO_FATAL_FAILURE(
ConnectServerL2cap(test_address_0, acl_handle_0, lcid_0));
ASSERT_NO_FATAL_FAILURE(ConnectServerPort(test_address_0, server_handle_0,
test_scn, test_mtu, acl_handle_0,
lcid_0, 0));
// Service 1
static const RawAddress test_address_1 = GetTestAddress(1);
static const uint16_t acl_handle_1 = 0x0012;
static const uint16_t lcid_1 = 0x0045;
uint16_t server_handle_1 = 0;
ASSERT_NO_FATAL_FAILURE(StartServerPort(test_uuid, test_scn, test_mtu,
port_mgmt_cback_1, port_event_cback_1,
&server_handle_1));
ASSERT_NO_FATAL_FAILURE(
ConnectServerL2cap(test_address_1, acl_handle_1, lcid_1));
ASSERT_NO_FATAL_FAILURE(ConnectServerPort(test_address_1, server_handle_1,
test_scn, test_mtu, acl_handle_1,
lcid_1, 1));
// Use service 0
ASSERT_NO_FATAL_FAILURE(ReceiveAndVerifyIncomingTransmission(
server_handle_0, false, test_scn, true, "Hello World0!\r", 50,
acl_handle_0, lcid_0, 0));
ASSERT_NO_FATAL_FAILURE(SendAndVerifyOutgoingTransmission(
server_handle_0, false, test_scn, false, "\r!0dlroW olleH", 4,
acl_handle_0, lcid_0));
// Use service 1
ASSERT_NO_FATAL_FAILURE(ReceiveAndVerifyIncomingTransmission(
server_handle_1, false, test_scn, true, "Hello World1!\r", 50,
acl_handle_1, lcid_1, 1));
ASSERT_NO_FATAL_FAILURE(SendAndVerifyOutgoingTransmission(
server_handle_1, false, test_scn, false, "\r!1dlroW olleH", 4,
acl_handle_1, lcid_1));
}
TEST_F(StackRfcommTest, SingleClientConnectionHelloWorld) {
static const uint16_t acl_handle = 0x0009;
static const uint16_t lcid = 0x0054;
static const uint16_t test_uuid = 0x1112;
static const uint8_t test_scn = 8;
static const uint16_t test_mtu = 1600;
static const RawAddress test_address = GetTestAddress(0);
uint16_t client_handle = 0;
ASSERT_NO_FATAL_FAILURE(StartClientPort(
test_address, test_uuid, test_scn, test_mtu, port_mgmt_cback_0,
port_event_cback_0, lcid, acl_handle, &client_handle, true));
ASSERT_NO_FATAL_FAILURE(TestConnectClientPortL2cap(acl_handle, lcid));
ASSERT_NO_FATAL_FAILURE(ConnectClientPort(test_address, client_handle,
test_scn, test_mtu, acl_handle,
lcid, 0, true));
ASSERT_NO_FATAL_FAILURE(SendAndVerifyOutgoingTransmission(
client_handle, false, test_scn, true, "\r!dlroW olleH", -1, acl_handle,
lcid));
ASSERT_NO_FATAL_FAILURE(ReceiveAndVerifyIncomingTransmission(
client_handle, false, test_scn, false, "Hello World!\r", -1, acl_handle,
lcid, 0));
}
TEST_F(StackRfcommTest, MultiClientPortSameDeviceHelloWorld) {
static const uint16_t acl_handle = 0x0009;
static const uint16_t lcid = 0x0054;
static const uint16_t test_mtu = 1600;
static const RawAddress test_address = GetTestAddress(0);
// Connection 0
static const uint16_t test_uuid_0 = 0x1112;
static const uint8_t test_scn_0 = 8;
uint16_t client_handle_0 = 0;
ASSERT_NO_FATAL_FAILURE(StartClientPort(
test_address, test_uuid_0, test_scn_0, test_mtu, port_mgmt_cback_0,
port_event_cback_0, lcid, acl_handle, &client_handle_0, true));
ASSERT_NO_FATAL_FAILURE(TestConnectClientPortL2cap(acl_handle, lcid));
ASSERT_NO_FATAL_FAILURE(ConnectClientPort(test_address, client_handle_0,
test_scn_0, test_mtu, acl_handle,
lcid, 0, true));
// Connection 1
static const uint16_t test_uuid_1 = 0x111F;
static const uint8_t test_scn_1 = 10;
uint16_t client_handle_1 = 0;
ASSERT_NO_FATAL_FAILURE(StartClientPort(
test_address, test_uuid_1, test_scn_1, test_mtu, port_mgmt_cback_1,
port_event_cback_1, lcid, acl_handle, &client_handle_1, false));
ASSERT_NO_FATAL_FAILURE(ConnectClientPort(test_address, client_handle_1,
test_scn_1, test_mtu, acl_handle,
lcid, 1, false));
// Use connection 0
ASSERT_NO_FATAL_FAILURE(SendAndVerifyOutgoingTransmission(
client_handle_0, false, test_scn_0, true, "\r!dlroW olleH", -1,
acl_handle, lcid));
ASSERT_NO_FATAL_FAILURE(ReceiveAndVerifyIncomingTransmission(
client_handle_0, false, test_scn_0, false, "Hello World!\r", -1,
acl_handle, lcid, 0));
// Use connection 1
ASSERT_NO_FATAL_FAILURE(SendAndVerifyOutgoingTransmission(
client_handle_1, false, test_scn_1, true, "\r!dlroW olleH", -1,
acl_handle, lcid));
ASSERT_NO_FATAL_FAILURE(ReceiveAndVerifyIncomingTransmission(
client_handle_1, false, test_scn_1, false, "Hello World!\r", -1,
acl_handle, lcid, 1));
}
TEST_F(StackRfcommTest, SameClientPortMultiDeviceHelloWorld) {
static const uint16_t test_uuid = 0x1112;
static const uint8_t test_scn = 8;
static const uint16_t test_mtu = 1600;
// Connection 0
static const RawAddress test_address_0 = GetTestAddress(0);
static const uint16_t acl_handle_0 = 0x0009;
static const uint16_t lcid_0 = 0x0054;
uint16_t client_handle_0 = 0;
ASSERT_NO_FATAL_FAILURE(StartClientPort(
test_address_0, test_uuid, test_scn, test_mtu, port_mgmt_cback_0,
port_event_cback_0, lcid_0, acl_handle_0, &client_handle_0, true));
ASSERT_NO_FATAL_FAILURE(TestConnectClientPortL2cap(acl_handle_0, lcid_0));
ASSERT_NO_FATAL_FAILURE(ConnectClientPort(test_address_0, client_handle_0,
test_scn, test_mtu, acl_handle_0,
lcid_0, 0, true));
// Connection 1
static const RawAddress test_address_1 = GetTestAddress(1);
static const uint16_t acl_handle_1 = 0x0012;
static const uint16_t lcid_1 = 0x0045;
uint16_t client_handle_1 = 0;
ASSERT_NO_FATAL_FAILURE(StartClientPort(
test_address_1, test_uuid, test_scn, test_mtu, port_mgmt_cback_1,
port_event_cback_1, lcid_1, acl_handle_1, &client_handle_1, true));
ASSERT_NO_FATAL_FAILURE(TestConnectClientPortL2cap(acl_handle_1, lcid_1));
ASSERT_NO_FATAL_FAILURE(ConnectClientPort(test_address_1, client_handle_1,
test_scn, test_mtu, acl_handle_1,
lcid_1, 1, true));
// Use connection 0
ASSERT_NO_FATAL_FAILURE(SendAndVerifyOutgoingTransmission(
client_handle_0, false, test_scn, true, "\r!dlroW olleH", -1,
acl_handle_0, lcid_0));
ASSERT_NO_FATAL_FAILURE(ReceiveAndVerifyIncomingTransmission(
client_handle_0, false, test_scn, false, "Hello World!\r", -1,
acl_handle_0, lcid_0, 0));
// Use connection 1
ASSERT_NO_FATAL_FAILURE(SendAndVerifyOutgoingTransmission(
client_handle_1, false, test_scn, true, "\r!dlroW olleH", -1,
acl_handle_1, lcid_1));
ASSERT_NO_FATAL_FAILURE(ReceiveAndVerifyIncomingTransmission(
client_handle_1, false, test_scn, false, "Hello World!\r", -1,
acl_handle_1, lcid_1, 1));
}
TEST_F(StackRfcommTest, TestConnectionCollision) {
static const uint16_t acl_handle = 0x0008;
static const uint16_t old_lcid = 0x004a;
static const uint16_t new_lcid = 0x005c;
static const uint16_t test_uuid = 0x1112;
static const uint8_t test_server_scn = 3;
static const uint8_t test_peer_scn = 10;
// Must be smaller than L2CAP_MTU_SIZE by at least 4 bytes
static const uint16_t test_mtu = 1000;
static const RawAddress test_address = GetTestAddress(0);
uint16_t server_handle = 0;
VLOG(1) << "Step 1";
// Prepare a server port
int status = RFCOMM_CreateConnection(test_uuid, test_server_scn, true,
test_mtu, RawAddress::kAny,
&server_handle, port_mgmt_cback_0);
ASSERT_EQ(status, PORT_SUCCESS);
status = PORT_SetEventMask(server_handle, PORT_EV_RXCHAR);
ASSERT_EQ(status, PORT_SUCCESS);
status = PORT_SetEventCallback(server_handle, port_event_cback_0);
ASSERT_EQ(status, PORT_SUCCESS);
VLOG(1) << "Step 2";
// Try to connect to a client port
uint16_t client_handle_1 = 0;
EXPECT_CALL(l2cap_interface_, ConnectRequest(BT_PSM_RFCOMM, test_address))
.Times(1)
.WillOnce(Return(old_lcid));
status = RFCOMM_CreateConnection(test_uuid, test_peer_scn, false, test_mtu,
test_address, &client_handle_1,
port_mgmt_cback_1);
ASSERT_EQ(status, PORT_SUCCESS);
status = PORT_SetEventMask(client_handle_1, PORT_EV_RXCHAR);
ASSERT_EQ(status, PORT_SUCCESS);
status = PORT_SetEventCallback(client_handle_1, port_event_cback_1);
ASSERT_EQ(status, PORT_SUCCESS);
VLOG(1) << "Step 3";
// While our connection is pending, remote device tries to connect to
// new_lcid, with L2CAP command id: pending_cmd_id
static const uint8_t pending_cmd_id = 0x05;
// RFCOMM starts timer for collision:
l2cap_appl_info_.pL2CA_ConnectInd_Cb(test_address, new_lcid, BT_PSM_RFCOMM,
pending_cmd_id);
VLOG(1) << "Step 4";
// Remote reject our connection request saying PSM not allowed
// This should trigger RFCOMM to accept remote L2CAP connection at new_lcid
EXPECT_CALL(l2cap_interface_, ConnectResponse(test_address, pending_cmd_id,
new_lcid, L2CAP_CONN_OK, 0))
.WillOnce(Return(true));
// Followed by configure request for MTU size
tL2CAP_CFG_INFO our_cfg_req = {.mtu_present = true, .mtu = L2CAP_MTU_SIZE};
EXPECT_CALL(l2cap_interface_,
ConfigRequest(new_lcid, PointerMemoryEqual(&our_cfg_req)))
.WillOnce(Return(true));
l2cap_appl_info_.pL2CA_ConnectCfm_Cb(old_lcid, L2CAP_CONN_NO_PSM);
VLOG(1) << "Step 5";
// Remote device also ask to configure MTU size as well
tL2CAP_CFG_INFO peer_cfg_req = {.mtu_present = true, .mtu = test_mtu};
// We responded by saying OK
tL2CAP_CFG_INFO our_cfg_rsp = {.result = L2CAP_CFG_OK,
.mtu = peer_cfg_req.mtu};
EXPECT_CALL(l2cap_interface_,
ConfigResponse(new_lcid, PointerMemoryEqual(&our_cfg_rsp)))
.WillOnce(Return(true));
l2cap_appl_info_.pL2CA_ConfigInd_Cb(new_lcid, &peer_cfg_req);
VLOG(1) << "Step 6";
// Remote device accepted our MTU size
tL2CAP_CFG_INFO peer_cfg_rsp = {.mtu_present = true, .mtu = L2CAP_MTU_SIZE};
l2cap_appl_info_.pL2CA_ConfigCfm_Cb(new_lcid, &peer_cfg_rsp);
// L2CAP collision and connection setup done
VLOG(1) << "Step 7";
// Remote device connect multiplexer channel
BT_HDR* sabm_channel_0 = AllocateWrappedIncomingL2capAclPacket(
CreateQuickSabmPacket(RFCOMM_MX_DLCI, new_lcid, acl_handle));
// We accept
BT_HDR* ua_channel_0 = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickUaPacket(RFCOMM_MX_DLCI, new_lcid, acl_handle));
EXPECT_CALL(l2cap_interface_, DataWrite(new_lcid, BtHdrEqual(ua_channel_0)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
// And immediately try to configure test_peer_scn
BT_HDR* uih_pn_cmd_to_peer = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickPnPacket(false, GetDlci(true, test_peer_scn), true, test_mtu,
RFCOMM_PN_CONV_LAYER_CBFC_I >> 4, 0, RFCOMM_K_MAX,
new_lcid, acl_handle));
EXPECT_CALL(l2cap_interface_,
DataWrite(new_lcid, BtHdrEqual(uih_pn_cmd_to_peer)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
// Packet should be freed by this method
l2cap_appl_info_.pL2CA_DataInd_Cb(new_lcid, sabm_channel_0);
osi_free(ua_channel_0);
osi_free(uih_pn_cmd_to_peer);
VLOG(1) << "Step 8";
// Peer tries to configure test_server_scn
BT_HDR* uih_pn_cmd_from_peer = AllocateWrappedIncomingL2capAclPacket(
CreateQuickPnPacket(true, GetDlci(false, test_server_scn), true, test_mtu,
RFCOMM_PN_CONV_LAYER_CBFC_I >> 4, 0, RFCOMM_K_MAX,
new_lcid, acl_handle));
// We, as acceptor, must accept
BT_HDR* uih_pn_rsp_to_peer = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickPnPacket(false, GetDlci(false, test_server_scn), false,
test_mtu, RFCOMM_PN_CONV_LAYER_CBFC_R >> 4, 0,
RFCOMM_K_MAX, new_lcid, acl_handle));
EXPECT_CALL(l2cap_interface_,
DataWrite(new_lcid, BtHdrEqual(uih_pn_rsp_to_peer)))
.Times(1)
.WillOnce(Return(L2CAP_DW_SUCCESS));
l2cap_appl_info_.pL2CA_DataInd_Cb(new_lcid, uih_pn_cmd_from_peer);
osi_free(uih_pn_rsp_to_peer);
VLOG(1) << "Step 9";
// Remote never replies our configuration request for test_peer_scn
// But instead connect to test_server_scn directly
BT_HDR* sabm_server_scn =
AllocateWrappedIncomingL2capAclPacket(CreateQuickSabmPacket(
GetDlci(false, test_server_scn), new_lcid, acl_handle));
// We must do security check first
tBTM_SEC_CALLBACK* security_callback = nullptr;
void* p_port = nullptr;
EXPECT_CALL(btm_security_internal_interface_,
MultiplexingProtocolAccessRequest(
test_address, BT_PSM_RFCOMM, false, BTM_SEC_PROTO_RFCOMM,
test_server_scn, NotNull(), NotNull()))
.WillOnce(DoAll(SaveArg<5>(&security_callback), SaveArg<6>(&p_port),
Return(BTM_SUCCESS)));
l2cap_appl_info_.pL2CA_DataInd_Cb(new_lcid, sabm_server_scn);
VLOG(1) << "Step 10";
// After security check, we accept the connection
ASSERT_TRUE(security_callback);
BT_HDR* ua_server_scn =
AllocateWrappedOutgoingL2capAclPacket(CreateQuickUaPacket(
GetDlci(false, test_server_scn), new_lcid, acl_handle));
EXPECT_CALL(l2cap_interface_, DataWrite(new_lcid, BtHdrEqual(ua_server_scn)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
// Callback should come from server port instead, client port will timeout
// in 20 seconds
EXPECT_CALL(rfcomm_callback_,
PortManagementCallback(PORT_SUCCESS, server_handle, 0));
security_callback(&test_address, BT_TRANSPORT_BR_EDR, p_port, BTM_SUCCESS);
osi_free(ua_server_scn);
VLOG(1) << "Step 11";
// MPX_CTRL Modem Status Command (MSC)
BT_HDR* uih_msc_cmd_from_peer = AllocateWrappedIncomingL2capAclPacket(
CreateQuickMscPacket(true, GetDlci(false, test_server_scn), new_lcid,
acl_handle, true, false, true, true, false, true));
BT_HDR* uih_msc_rsp_to_peer = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickMscPacket(false, GetDlci(false, test_server_scn), new_lcid,
acl_handle, false, false, true, true, false, true));
// MPX_CTRL Modem Status Response (MSC)
EXPECT_CALL(l2cap_interface_,
DataWrite(new_lcid, BtHdrEqual(uih_msc_rsp_to_peer)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
BT_HDR* uih_msc_cmd_to_peer = AllocateWrappedOutgoingL2capAclPacket(
CreateQuickMscPacket(false, GetDlci(false, test_server_scn), new_lcid,
acl_handle, true, false, true, true, false, true));
EXPECT_CALL(l2cap_interface_,
DataWrite(new_lcid, BtHdrEqual(uih_msc_cmd_to_peer)))
.WillOnce(Return(L2CAP_DW_SUCCESS));
l2cap_appl_info_.pL2CA_DataInd_Cb(new_lcid, uih_msc_cmd_from_peer);
osi_free(uih_msc_rsp_to_peer);
osi_free(uih_msc_cmd_to_peer);
VLOG(1) << "Step 12";
BT_HDR* uih_msc_rsp_from_peer = AllocateWrappedIncomingL2capAclPacket(
CreateQuickMscPacket(true, GetDlci(false, test_server_scn), new_lcid,
acl_handle, false, false, true, true, false, true));
l2cap_appl_info_.pL2CA_DataInd_Cb(new_lcid, uih_msc_rsp_from_peer);
}
} // namespace