diff --git a/system/bta/ag/bta_ag_act.cc b/system/bta/ag/bta_ag_act.cc index 34ce19128cca8e2462c52ee1e6ca294fed2300b5..f0ccef204c050a01e9d3bbc3190a208410e54fce 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 487664f1281c6983c0f1872e3691c2b219a2badf..3082f075433a98c038e4d74c9e92a7ec0c260116 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 0f33853213cfb92fbca49cc87d2f1ff361d4c289..dd0a55d2277b511a236a5169ebd594cdd8eef5cf 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 5573160e6b728020a5cb6d155ad14c44fe0c27cd..a3306ecc368602b46f3ce50ddf6dd632c78b51ab 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 1218c9120065eb9904cffa3ca73607729532c358..77983757f709233fafdc50a7bd86034655b3acee 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 8d3d8859dded496829615e88c37d474d80ae3414..3d9da3f34534ce11eeec42b47f6355896b78d972 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