From 29687e211a54ee1bf37561877d9b3fea35344dc2 Mon Sep 17 00:00:00 2001 From: Jack He <siyuanh@google.com> Date: Sun, 22 Apr 2018 17:30:14 -0700 Subject: [PATCH] HFP: Fix connection colision handling * When there is a connection collison, and remote device is trying to connect to HFP server channel while we try to connect to remote's server channel as a client. If the remote device succssfully connect to us, we should drop our connection request to remote device and accept remote device's connection request. As result, we will observe 1. Our connection request fails, this propagate up to Java layer and also clean up any states in these layers 2. Remote connection request succeeds, this propagate up to Java layer as well as a new incoming connection * Added BTA_AG_COLLISION_EVT to formally handle AG collision in state machine * Modified bta_ag_resume_open to re-send BTA_AG_API_OPEN_EVT instead of modifying the state machine state directly. State machine states should only be modified by state machine handler * Ignore RFCOMM open event if PORT_CheckConnection fails * Added StackRfcommTest.TestConnectionCollision to reproduce this scenario with 100% certainty * Add static checks to HFP BTA state machine to make sure action and event indices match enum sizes Bug: 77224743 Test: connect to multiple remote devices with HFP, MAP and PBAP StackRfcommTest.TestConnectionCollision btestplans/details/158641/3975 Change-Id: Ib3652784b123abe195e7bd30421ef5f4345b1d4d --- system/bta/ag/bta_ag_act.cc | 74 +++++--- system/bta/ag/bta_ag_int.h | 4 +- system/bta/ag/bta_ag_main.cc | 114 ++++------- system/btif/src/btif_hf.cc | 11 +- system/stack/rfcomm/port_utils.cc | 7 +- system/stack/test/rfcomm/stack_rfcomm_test.cc | 177 ++++++++++++++++++ 6 files changed, 279 insertions(+), 108 deletions(-) diff --git a/system/bta/ag/bta_ag_act.cc b/system/bta/ag/bta_ag_act.cc index 34ce19128cc..f0ccef204c0 100644 --- a/system/bta/ag/bta_ag_act.cc +++ b/system/bta/ag/bta_ag_act.cc @@ -174,12 +174,9 @@ void bta_ag_start_dereg(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) { * ******************************************************************************/ void bta_ag_start_open(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) { - /* store parameters */ - if (!data.IsEmpty()) { - p_scb->peer_addr = data.api_open.bd_addr; - p_scb->open_services = data.api_open.services; - p_scb->cli_sec_mask = data.api_open.sec_mask; - } + p_scb->peer_addr = data.api_open.bd_addr; + p_scb->open_services = data.api_open.services; + p_scb->cli_sec_mask = data.api_open.sec_mask; /* Check if RFCOMM has any incoming connection to avoid collision. */ RawAddress pending_bd_addr = RawAddress::kEmpty; @@ -329,6 +326,7 @@ void bta_ag_open_fail(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) { * ******************************************************************************/ void bta_ag_rfc_fail(tBTA_AG_SCB* p_scb, UNUSED_ATTR const tBTA_AG_DATA& data) { + RawAddress peer_addr = p_scb->peer_addr; /* reinitialize stuff */ p_scb->conn_handle = 0; p_scb->conn_service = 0; @@ -345,7 +343,7 @@ void bta_ag_rfc_fail(tBTA_AG_SCB* p_scb, UNUSED_ATTR const tBTA_AG_DATA& data) { bta_ag_start_servers(p_scb, p_scb->reg_services); /* call open cback w. failure */ - bta_ag_cback_open(p_scb, RawAddress::kEmpty, BTA_AG_FAIL_RFCOMM); + bta_ag_cback_open(p_scb, peer_addr, BTA_AG_FAIL_RFCOMM); } /******************************************************************************* @@ -512,29 +510,34 @@ void bta_ag_rfc_acp_open(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) { int status = PORT_CheckConnection(data.rfc.port_handle, &dev_addr, &lcid); if (status != PORT_SUCCESS) { LOG(ERROR) << __func__ << ", PORT_CheckConnection returned " << status; + return; } /* Collision Handling */ for (tBTA_AG_SCB& ag_scb : bta_ag_cb.scb) { + // Cancel any pending collision timers if (ag_scb.in_use && alarm_is_scheduled(ag_scb.collision_timer)) { alarm_cancel(ag_scb.collision_timer); - if (dev_addr == ag_scb.peer_addr) { - /* If incoming and outgoing device are same, nothing more to do. */ - /* Outgoing conn will be aborted because we have successful incoming - * conn. */ - } else { - /* Resume outgoing connection. */ - tBTA_AG_SCB* other_scb = bta_ag_get_other_idle_scb(p_scb); - if (other_scb) { - other_scb->peer_addr = ag_scb.peer_addr; - other_scb->open_services = ag_scb.open_services; - other_scb->cli_sec_mask = ag_scb.cli_sec_mask; - bta_ag_resume_open(other_scb); - } + if (dev_addr != ag_scb.peer_addr && p_scb != &ag_scb) { + // Resume outgoing connection if incoming is not on the same device + bta_ag_resume_open(&ag_scb); } - break; } + if (dev_addr == ag_scb.peer_addr && p_scb != &ag_scb) { + // Fail the outgoing connection to clean up any upper layer states + bta_ag_rfc_fail(&ag_scb, tBTA_AG_DATA::kEmpty); + // If client port is opened, close it + if (ag_scb.conn_handle > 0) { + status = RFCOMM_RemoveConnection(ag_scb.conn_handle); + if (status != PORT_SUCCESS) { + LOG(WARNING) << __func__ << ": RFCOMM_RemoveConnection failed for " + << dev_addr << ", handle " + << std::to_string(ag_scb.conn_handle) << ", error " + << status; + } + } + } } p_scb->peer_addr = dev_addr; @@ -802,3 +805,32 @@ void bta_ag_setcodec(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) { (*bta_ag_cb.p_cback)(BTA_AG_WBS_EVT, (tBTA_AG*)&val); } + +static void bta_ag_collision_timer_cback(void* data) { + if (data == nullptr) { + LOG(ERROR) << __func__ << ": data should never be null in a timer callback"; + return; + } + /* If the peer haven't opened AG connection */ + /* we will restart opening process. */ + bta_ag_resume_open(static_cast<tBTA_AG_SCB*>(data)); +} + +void bta_ag_handle_collision(tBTA_AG_SCB* p_scb, + UNUSED_ATTR const tBTA_AG_DATA& data) { + /* Cancel SDP if it had been started. */ + if (p_scb->p_disc_db) { + SDP_CancelServiceSearch(p_scb->p_disc_db); + bta_ag_free_db(p_scb, tBTA_AG_DATA::kEmpty); + } + + /* reopen registered servers */ + /* Collision may be detected before or after we close servers. */ + if (bta_ag_is_server_closed(p_scb)) { + bta_ag_start_servers(p_scb, p_scb->reg_services); + } + + /* Start timer to han */ + alarm_set_on_mloop(p_scb->collision_timer, BTA_AG_COLLISION_TIMEOUT_MS, + bta_ag_collision_timer_cback, p_scb); +} \ No newline at end of file diff --git a/system/bta/ag/bta_ag_int.h b/system/bta/ag/bta_ag_int.h index 487664f1281..3082f075433 100644 --- a/system/bta/ag/bta_ag_int.h +++ b/system/bta/ag/bta_ag_int.h @@ -79,6 +79,7 @@ enum { BTA_AG_DISC_FAIL_EVT, BTA_AG_RING_TIMEOUT_EVT, BTA_AG_SVC_TIMEOUT_EVT, + BTA_AG_COLLISION_EVT, BTA_AG_MAX_EVT, }; @@ -306,7 +307,6 @@ extern uint8_t bta_ag_service_to_idx(tBTA_SERVICE_MASK services); extern uint16_t bta_ag_idx_by_bdaddr(const RawAddress* peer_addr); extern bool bta_ag_other_scb_open(tBTA_AG_SCB* p_curr_scb); extern bool bta_ag_scb_open(tBTA_AG_SCB* p_curr_scb); -extern tBTA_AG_SCB* bta_ag_get_other_idle_scb(tBTA_AG_SCB* p_curr_scb); extern void bta_ag_sm_execute(tBTA_AG_SCB* p_scb, uint16_t event, const tBTA_AG_DATA& data); extern void bta_ag_sm_execute_by_handle(uint16_t handle, uint16_t event, @@ -378,6 +378,8 @@ extern void bta_ag_svc_conn_open(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data); extern void bta_ag_result(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data); extern void bta_ag_setcodec(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data); extern void bta_ag_send_ring(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data); +extern void bta_ag_handle_collision(tBTA_AG_SCB* p_scb, + const tBTA_AG_DATA& data); /* Internal utility functions */ extern void bta_ag_sco_codec_nego(tBTA_AG_SCB* p_scb, bool result); diff --git a/system/bta/ag/bta_ag_main.cc b/system/bta/ag/bta_ag_main.cc index 0f33853213c..dd0a55d2277 100644 --- a/system/bta/ag/bta_ag_main.cc +++ b/system/bta/ag/bta_ag_main.cc @@ -68,6 +68,7 @@ enum { BTA_AG_RESULT, BTA_AG_SETCODEC, BTA_AG_SEND_RING, + BTA_AG_HANDLE_COLLISION, BTA_AG_NUM_ACTIONS }; @@ -133,6 +134,7 @@ static const char* bta_ag_evt_str(uint16_t event) { CASE_RETURN_STR(BTA_AG_DISC_FAIL_EVT) CASE_RETURN_STR(BTA_AG_RING_TIMEOUT_EVT) CASE_RETURN_STR(BTA_AG_SVC_TIMEOUT_EVT) + CASE_RETURN_STR(BTA_AG_COLLISION_EVT) default: return "Unknown AG Event"; } @@ -160,7 +162,11 @@ const tBTA_AG_ACTION bta_ag_action[] = { bta_ag_sco_conn_close, bta_ag_sco_listen, bta_ag_sco_open, bta_ag_sco_close, bta_ag_sco_shutdown, bta_ag_post_sco_open, bta_ag_post_sco_close, bta_ag_svc_conn_open, bta_ag_result, - bta_ag_setcodec, bta_ag_send_ring}; + bta_ag_setcodec, bta_ag_send_ring, bta_ag_handle_collision}; + +static_assert(sizeof(bta_ag_action) / sizeof(tBTA_AG_ACTION) == + BTA_AG_NUM_ACTIONS, + "bta_ag_action must handle all actions"); /* state table information */ #define BTA_AG_ACTIONS 2 /* number of actions */ @@ -189,7 +195,8 @@ const uint8_t bta_ag_st_init[][BTA_AG_NUM_COLS] = { /* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, /* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, /* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, - /* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}}; + /* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}, + /* COLLISION_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_INIT_ST}}; /* state table for opening state */ const uint8_t bta_ag_st_opening[][BTA_AG_NUM_COLS] = { @@ -216,7 +223,9 @@ const uint8_t bta_ag_st_opening[][BTA_AG_NUM_COLS] = { /* DISC_OK_EVT */ {BTA_AG_RFC_DO_OPEN, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, /* DISC_FAIL_EVT */ {BTA_AG_DISC_FAIL, BTA_AG_IGNORE, BTA_AG_INIT_ST}, /* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, - /* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}}; + /* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPENING_ST}, + /* COLLISION_EVT */ + {BTA_AG_HANDLE_COLLISION, BTA_AG_IGNORE, BTA_AG_INIT_ST}}; /* state table for open state */ const uint8_t bta_ag_st_open[][BTA_AG_NUM_COLS] = { @@ -243,7 +252,8 @@ const uint8_t bta_ag_st_open[][BTA_AG_NUM_COLS] = { /* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, /* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, /* RING_TOUT_EVT */ {BTA_AG_SEND_RING, BTA_AG_IGNORE, BTA_AG_OPEN_ST}, - /* SVC_TOUT_EVT */ {BTA_AG_START_CLOSE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}}; + /* SVC_TOUT_EVT */ {BTA_AG_START_CLOSE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, + /* COLLISION_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_OPEN_ST}}; /* state table for closing state */ const uint8_t bta_ag_st_closing[][BTA_AG_NUM_COLS] = { @@ -269,7 +279,19 @@ const uint8_t bta_ag_st_closing[][BTA_AG_NUM_COLS] = { /* DISC_OK_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, /* DISC_FAIL_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, /* RING_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, - /* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}}; + /* SVC_TOUT_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}, + /* COLLISION_EVT */ {BTA_AG_IGNORE, BTA_AG_IGNORE, BTA_AG_CLOSING_ST}}; + +constexpr size_t BTA_AG_NUM_EVENTS = + BTA_AG_MAX_EVT - BTA_SYS_EVT_START(BTA_ID_AG); +static_assert(sizeof(bta_ag_st_init) / BTA_AG_NUM_COLS == BTA_AG_NUM_EVENTS, + "bta_ag_st_init must handle all AG events"); +static_assert(sizeof(bta_ag_st_opening) / BTA_AG_NUM_COLS == BTA_AG_NUM_EVENTS, + "bta_ag_st_opening must handle all AG events"); +static_assert(sizeof(bta_ag_st_open) / BTA_AG_NUM_COLS == BTA_AG_NUM_EVENTS, + "bta_ag_st_open must handle all AG events"); +static_assert(sizeof(bta_ag_st_closing) / BTA_AG_NUM_COLS == BTA_AG_NUM_EVENTS, + "bta_ag_st_closing must handle all AG events"); /* type for state table */ typedef const uint8_t (*tBTA_AG_ST_TBL)[BTA_AG_NUM_COLS]; @@ -494,47 +516,6 @@ bool bta_ag_scb_open(tBTA_AG_SCB* p_curr_scb) { p_curr_scb->state == BTA_AG_OPEN_ST; } -/******************************************************************************* - * - * Function bta_ag_get_other_idle_scb - * - * Description Return other scb if it is in INIT st. - * - * - * Returns Pointer to other scb if INIT st, NULL otherwise. - * - ******************************************************************************/ -tBTA_AG_SCB* bta_ag_get_other_idle_scb(tBTA_AG_SCB* p_curr_scb) { - for (tBTA_AG_SCB& scb : bta_ag_cb.scb) { - if (scb.in_use && (&scb != p_curr_scb) && (scb.state == BTA_AG_INIT_ST)) { - return &scb; - } - } - /* no other scb found */ - APPL_TRACE_DEBUG("bta_ag_get_other_idle_scb: No idle AG scb"); - return nullptr; -} - -/******************************************************************************* - * - * Function bta_ag_collision_timer_cback - * - * Description AG connection collision timer callback - * - * - * Returns void - * - ******************************************************************************/ -static void bta_ag_collision_timer_cback(void* data) { - tBTA_AG_SCB* p_scb = (tBTA_AG_SCB*)data; - - APPL_TRACE_DEBUG("%s", __func__); - - /* If the peer haven't opened AG connection */ - /* we will restart opening process. */ - bta_ag_resume_open(p_scb); -} - /******************************************************************************* * * Function bta_ag_collision_cback @@ -563,24 +544,7 @@ void bta_ag_collision_cback(UNUSED_ATTR tBTA_SYS_CONN_STATUS status, uint8_t id, LOG(WARNING) << __func__ << ": AG found collision (UNKNOWN) for handle " << unsigned(handle) << " device " << peer_addr; } - - p_scb->state = BTA_AG_INIT_ST; - - /* Cancel SDP if it had been started. */ - if (p_scb->p_disc_db) { - SDP_CancelServiceSearch(p_scb->p_disc_db); - bta_ag_free_db(p_scb, tBTA_AG_DATA::kEmpty); - } - - /* reopen registered servers */ - /* Collision may be detected before or after we close servers. */ - if (bta_ag_is_server_closed(p_scb)) { - bta_ag_start_servers(p_scb, p_scb->reg_services); - } - - /* Start timer to han */ - alarm_set_on_mloop(p_scb->collision_timer, BTA_AG_COLLISION_TIMEOUT_MS, - bta_ag_collision_timer_cback, p_scb); + bta_ag_sm_execute(p_scb, BTA_AG_COLLISION_EVT, tBTA_AG_DATA::kEmpty); } } @@ -595,20 +559,16 @@ void bta_ag_collision_cback(UNUSED_ATTR tBTA_SYS_CONN_STATUS status, uint8_t id, * ******************************************************************************/ void bta_ag_resume_open(tBTA_AG_SCB* p_scb) { - if (p_scb) { - APPL_TRACE_DEBUG("%s: handle=%d, bd_addr=%s", __func__, - bta_ag_scb_to_idx(p_scb), - p_scb->peer_addr.ToString().c_str()); - /* resume opening process. */ - if (p_scb->state == BTA_AG_INIT_ST) { - LOG(WARNING) << __func__ - << ": handle=" << unsigned(bta_ag_scb_to_idx(p_scb)) - << ", bd_addr=" << p_scb->peer_addr; - p_scb->state = BTA_AG_OPENING_ST; - bta_ag_start_open(p_scb, tBTA_AG_DATA::kEmpty); - } + if (p_scb->state == BTA_AG_INIT_ST) { + LOG(INFO) << __func__ << ": Resume connection to " << p_scb->peer_addr + << ", handle" << bta_ag_scb_to_idx(p_scb); + tBTA_AG_DATA open_data = {.api_open.bd_addr = p_scb->peer_addr, + .api_open.services = p_scb->open_services, + .api_open.sec_mask = p_scb->cli_sec_mask}; + bta_ag_sm_execute(p_scb, BTA_AG_API_OPEN_EVT, open_data); } else { - LOG(ERROR) << __func__ << ": null p_scb"; + VLOG(1) << __func__ << ": device " << p_scb->peer_addr + << " is already in state " << std::to_string(p_scb->state); } } diff --git a/system/btif/src/btif_hf.cc b/system/btif/src/btif_hf.cc index 5573160e6b7..a3306ecc368 100644 --- a/system/btif/src/btif_hf.cc +++ b/system/btif/src/btif_hf.cc @@ -31,6 +31,7 @@ #include <cstring> #include <ctime> +#include <bta/include/bta_ag_api.h> #include <hardware/bluetooth.h> #include <hardware/bluetooth_headset_callbacks.h> #include <hardware/bluetooth_headset_interface.h> @@ -327,11 +328,11 @@ static void btif_hf_upstreams_evt(uint16_t event, char* p_param) { << unsigned(p_data->open.status); btif_hf_cb[idx].state = BTHF_CONNECTION_STATE_DISCONNECTED; } else { - BTIF_TRACE_WARNING( - "%s: AG open failed, but another device connected. status=%d " - "state=%d connected device=%s", - __func__, p_data->open.status, btif_hf_cb[idx].state, - btif_hf_cb[idx].connected_bda.ToString().c_str()); + LOG(WARNING) << __func__ << ": AG open failed for " + << p_data->open.bd_addr << ", error " + << std::to_string(p_data->open.status) + << ", local device is " << btif_hf_cb[idx].connected_bda + << ". Ignoring as not expecting to open"; break; } bt_hf_callbacks->ConnectionStateCallback(btif_hf_cb[idx].state, diff --git a/system/stack/rfcomm/port_utils.cc b/system/stack/rfcomm/port_utils.cc index 1218c912006..77983757f70 100644 --- a/system/stack/rfcomm/port_utils.cc +++ b/system/stack/rfcomm/port_utils.cc @@ -319,10 +319,9 @@ tPORT* port_find_mcb_dlci_port(tRFC_MCB* p_mcb, uint8_t dlci) { uint8_t handle = p_mcb->port_handles[dlci]; if (handle == 0) { - LOG(WARNING) << __func__ - << ": Cannot find allocated RFCOMM app port for DLCI " - << std::to_string(dlci) << " on " << p_mcb->bd_addr - << ", p_mcb=" << p_mcb; + LOG(INFO) << __func__ << ": Cannot find allocated RFCOMM app port for DLCI " + << std::to_string(dlci) << " on " << p_mcb->bd_addr + << ", p_mcb=" << p_mcb; return nullptr; } return &rfc_cb.port.port[handle - 1]; diff --git a/system/stack/test/rfcomm/stack_rfcomm_test.cc b/system/stack/test/rfcomm/stack_rfcomm_test.cc index 8d3d8859dde..3d9da3f3453 100644 --- a/system/stack/test/rfcomm/stack_rfcomm_test.cc +++ b/system/stack/test/rfcomm/stack_rfcomm_test.cc @@ -712,4 +712,181 @@ TEST_F(StackRfcommTest, SameClientPortMultiDeviceHelloWorld) { 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 -- GitLab