Merge changes from topics "bt-hfp-fix-connection-collision-handling", "bt-rfcomm-improve-logging-and-readability", "bt-rfcomm-add-end-to-end-unit-tests"
* changes:
HFP: Fix connection colision handling
RFCOMM: Improve logging and readability
RFCOMM: Add unit tests for connection scenarios
diff --git a/bta/ag/bta_ag_act.cc b/bta/ag/bta_ag_act.cc
index 75063ac..f0ccef2 100644
--- a/bta/ag/bta_ag_act.cc
+++ b/bta/ag/bta_ag_act.cc
@@ -174,17 +174,13 @@
*
******************************************************************************/
void bta_ag_start_open(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) {
- RawAddress pending_bd_addr = {};
-
- /* 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. */
- if (PORT_IsOpening(pending_bd_addr)) {
+ RawAddress pending_bd_addr = RawAddress::kEmpty;
+ if (PORT_IsOpening(&pending_bd_addr)) {
/* Let the incoming connection goes through. */
/* Issue collision for this scb for now. */
/* We will decide what to do when we find incoming connetion later. */
@@ -330,6 +326,7 @@
*
******************************************************************************/
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;
@@ -346,7 +343,7 @@
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);
}
/*******************************************************************************
@@ -502,56 +499,51 @@
*
******************************************************************************/
void bta_ag_rfc_acp_open(tBTA_AG_SCB* p_scb, const tBTA_AG_DATA& data) {
- uint16_t lcid;
- int i;
- tBTA_AG_SCB *ag_scb, *other_scb;
- RawAddress dev_addr = {};
- int status;
-
+ APPL_TRACE_DEBUG("%s: serv_handle0 = %d serv_handle1 = %d", __func__,
+ p_scb->serv_handle[0], p_scb->serv_handle[1]);
/* set role */
p_scb->role = BTA_AG_ACP;
- APPL_TRACE_DEBUG("bta_ag_rfc_acp_open: serv_handle0 = %d serv_handle1 = %d",
- p_scb->serv_handle[0], p_scb->serv_handle[1]);
-
/* get bd addr of peer */
- if (PORT_SUCCESS !=
- (status = PORT_CheckConnection(data.rfc.port_handle, dev_addr, &lcid))) {
- APPL_TRACE_DEBUG(
- "bta_ag_rfc_acp_open error PORT_CheckConnection returned status %d",
- status);
+ uint16_t lcid = 0;
+ RawAddress dev_addr = RawAddress::kEmpty;
+ 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 (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_MAX_NUM_CLIENTS;
- i++, ag_scb++) {
- 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. */
- 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);
+ 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 && 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;
}
}
-
- break;
}
}
p_scb->peer_addr = dev_addr;
/* determine connected service from port handle */
- for (i = 0; i < BTA_AG_NUM_IDX; i++) {
+ for (uint8_t i = 0; i < BTA_AG_NUM_IDX; i++) {
APPL_TRACE_DEBUG(
"bta_ag_rfc_acp_open: i = %d serv_handle = %d port_handle = %d", i,
p_scb->serv_handle[i], data.rfc.port_handle);
@@ -813,3 +805,32 @@
(*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/bta/ag/bta_ag_int.h b/bta/ag/bta_ag_int.h
index 487664f..3082f07 100644
--- a/bta/ag/bta_ag_int.h
+++ b/bta/ag/bta_ag_int.h
@@ -79,6 +79,7 @@
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 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_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/bta/ag/bta_ag_main.cc b/bta/ag/bta_ag_main.cc
index b63f459..dd0a55d 100644
--- a/bta/ag/bta_ag_main.cc
+++ b/bta/ag/bta_ag_main.cc
@@ -68,6 +68,7 @@
BTA_AG_RESULT,
BTA_AG_SETCODEC,
BTA_AG_SEND_RING,
+ BTA_AG_HANDLE_COLLISION,
BTA_AG_NUM_ACTIONS
};
@@ -133,6 +134,7 @@
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 @@
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 @@
/* 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 @@
/* 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 @@
/* 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 @@
/* 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];
@@ -496,52 +518,6 @@
/*******************************************************************************
*
- * 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) {
- tBTA_AG_SCB* p_scb = &bta_ag_cb.scb[0];
- uint8_t xx;
-
- for (xx = 0; xx < BTA_AG_MAX_NUM_CLIENTS; xx++, p_scb++) {
- if (p_scb->in_use && (p_scb != p_curr_scb) &&
- (p_scb->state == BTA_AG_INIT_ST)) {
- return p_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
*
* Description Get notified about collision.
@@ -559,33 +535,16 @@
if (p_scb && (p_scb->state == BTA_AG_OPENING_ST)) {
if (id == BTA_ID_SYS) {
- LOG(WARNING) << __func__ << "AG found collision (ACL) for handle "
+ LOG(WARNING) << __func__ << ": AG found collision (ACL) for handle "
<< unsigned(handle) << " device " << peer_addr;
} else if (id == BTA_ID_AG) {
- LOG(WARNING) << __func__ << "AG found collision (RFCOMM) for handle "
+ LOG(WARNING) << __func__ << ": AG found collision (RFCOMM) for handle "
<< unsigned(handle) << " device " << peer_addr;
} else {
- LOG(WARNING) << __func__ << "AG found collision (UNKNOWN) for handle "
+ 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);
}
}
@@ -600,20 +559,16 @@
*
******************************************************************************/
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/bta/av/bta_av_aact.cc b/bta/av/bta_av_aact.cc
index 9b07971..137e59c 100644
--- a/bta/av/bta_av_aact.cc
+++ b/bta/av/bta_av_aact.cc
@@ -264,11 +264,12 @@
* Returns void
*
******************************************************************************/
-static void bta_av_save_addr(tBTA_AV_SCB* p_scb, const RawAddress& b) {
+static void bta_av_save_addr(tBTA_AV_SCB* p_scb, const RawAddress& bd_addr) {
APPL_TRACE_DEBUG("%s: r:%d, s:%d", __func__, p_scb->recfg_sup,
p_scb->suspend_sup);
- if (p_scb->peer_addr != b) {
- APPL_TRACE_ERROR("%s: reset flags", __func__);
+ if (p_scb->peer_addr != bd_addr) {
+ LOG(INFO) << __func__ << ": reset flags old_addr=" << p_scb->peer_addr
+ << ", new_addr=" << bd_addr;
/* a new addr, reset the supported flags */
p_scb->recfg_sup = true;
p_scb->suspend_sup = true;
@@ -276,7 +277,7 @@
/* do this copy anyway, just in case the first addr matches
* the control block one by accident */
- p_scb->peer_addr = b;
+ p_scb->peer_addr = bd_addr;
}
/*******************************************************************************
diff --git a/bta/hf_client/bta_hf_client_act.cc b/bta/hf_client/bta_hf_client_act.cc
index 3e6c22d..e3f25f6 100644
--- a/bta/hf_client/bta_hf_client_act.cc
+++ b/bta/hf_client/bta_hf_client_act.cc
@@ -103,8 +103,8 @@
}
/* Check if RFCOMM has any incoming connection to avoid collision. */
- RawAddress pending_bd_addr;
- if (PORT_IsOpening(pending_bd_addr)) {
+ RawAddress pending_bd_addr = RawAddress::kEmpty;
+ if (PORT_IsOpening(&pending_bd_addr)) {
/* Let the incoming connection goes through. */
/* Issue collision for now. */
/* We will decide what to do when we find incoming connection later.*/
@@ -164,21 +164,17 @@
p_data->hdr.layer_specific);
return;
}
-
- uint16_t lcid;
- RawAddress dev_addr;
- int status;
-
/* set role */
client_cb->role = BTA_HF_CLIENT_ACP;
APPL_TRACE_DEBUG("%s: conn_handle %d", __func__, client_cb->conn_handle);
/* get bd addr of peer */
- if (PORT_SUCCESS != (status = PORT_CheckConnection(client_cb->conn_handle,
- dev_addr, &lcid))) {
- APPL_TRACE_DEBUG("%s: error PORT_CheckConnection returned status %d",
- __func__, status);
+ uint16_t lcid = 0;
+ RawAddress dev_addr = RawAddress::kEmpty;
+ int status = PORT_CheckConnection(client_cb->conn_handle, &dev_addr, &lcid);
+ if (status != PORT_SUCCESS) {
+ LOG(ERROR) << __func__ << ": PORT_CheckConnection returned " << status;
}
/* Collision Handling */
diff --git a/bta/hf_client/bta_hf_client_rfc.cc b/bta/hf_client/bta_hf_client_rfc.cc
index e261c16..f3e0947 100644
--- a/bta/hf_client/bta_hf_client_rfc.cc
+++ b/bta/hf_client/bta_hf_client_rfc.cc
@@ -98,9 +98,12 @@
APPL_TRACE_DEBUG("%s: allocating a new CB for incoming connection",
__func__);
// Find the BDADDR of the peer device
- RawAddress peer_addr;
- uint16_t lcid;
- PORT_CheckConnection(port_handle, peer_addr, &lcid);
+ RawAddress peer_addr = RawAddress::kEmpty;
+ uint16_t lcid = 0;
+ int status = PORT_CheckConnection(port_handle, &peer_addr, &lcid);
+ if (status != PORT_SUCCESS) {
+ LOG(ERROR) << __func__ << ": PORT_CheckConnection returned " << status;
+ }
// Since we accepted a remote request we should allocate a handle first.
uint16_t tmp_handle = -1;
diff --git a/bta/jv/bta_jv_act.cc b/bta/jv/bta_jv_act.cc
index 87e5052..7c8a655 100644
--- a/bta/jv/bta_jv_act.cc
+++ b/bta/jv/bta_jv_act.cc
@@ -480,7 +480,7 @@
static tBTA_JV_PM_CB* bta_jv_alloc_set_pm_profile_cb(uint32_t jv_handle,
tBTA_JV_PM_ID app_id) {
bool bRfcHandle = (jv_handle & BTA_JV_RFCOMM_MASK) != 0;
- RawAddress peer_bd_addr;
+ RawAddress peer_bd_addr = RawAddress::kEmpty;
int i, j;
tBTA_JV_PM_CB** pp_cb;
@@ -494,8 +494,9 @@
pp_cb = &bta_jv_cb.port_cb[j].p_pm_cb;
if (PORT_SUCCESS !=
PORT_CheckConnection(bta_jv_cb.port_cb[j].port_handle,
- peer_bd_addr, NULL))
+ &peer_bd_addr, NULL)) {
i = BTA_JV_PM_MAX_NUM;
+ }
break;
}
}
@@ -604,7 +605,7 @@
}
/** Disables the BT device manager free the resources used by java */
-void bta_jv_disable() { LOG(ERROR) << __func__; }
+void bta_jv_disable() { LOG(INFO) << __func__; }
/**
* We keep a list of PSM's that have been freed from JAVA, for reuse.
@@ -1254,7 +1255,7 @@
tBTA_JV_RFC_CB* p_cb = bta_jv_rfc_port_to_cb(port_handle);
tBTA_JV_PCB* p_pcb = bta_jv_rfc_port_to_pcb(port_handle);
tBTA_JV evt_data;
- RawAddress rem_bda;
+ RawAddress rem_bda = RawAddress::kEmpty;
uint16_t lcid;
tBTA_JV_RFCOMM_CBACK* p_cback; /* the callback function */
@@ -1264,7 +1265,7 @@
VLOG(2) << __func__ << ": code=" << code << ", port_handle=" << port_handle
<< ", handle=" << p_cb->handle;
- PORT_CheckConnection(port_handle, rem_bda, &lcid);
+ PORT_CheckConnection(port_handle, &rem_bda, &lcid);
if (code == PORT_SUCCESS) {
evt_data.rfc_open.handle = p_cb->handle;
@@ -1451,7 +1452,7 @@
tBTA_JV_PCB* p_pcb = bta_jv_rfc_port_to_pcb(port_handle);
tBTA_JV_RFC_CB* p_cb = bta_jv_rfc_port_to_cb(port_handle);
tBTA_JV evt_data;
- RawAddress rem_bda;
+ RawAddress rem_bda = RawAddress::kEmpty;
uint16_t lcid;
VLOG(2) << __func__ << ": code=" << code << ", port_handle=" << port_handle;
if (NULL == p_cb || NULL == p_cb->p_cback) {
@@ -1465,9 +1466,13 @@
<< ", handle=" << loghex(p_cb->handle) << ", p_pcb" << p_pcb
<< ", user=" << p_pcb->rfcomm_slot_id;
- PORT_CheckConnection(port_handle, rem_bda, &lcid);
+ int status = PORT_CheckConnection(port_handle, &rem_bda, &lcid);
int failed = true;
if (code == PORT_SUCCESS) {
+ if (status != PORT_SUCCESS) {
+ LOG(ERROR) << __func__ << ": PORT_CheckConnection returned " << status
+ << ", although port is supposed to be connected";
+ }
evt_data.rfc_srv_open.handle = p_pcb->handle;
evt_data.rfc_srv_open.status = BTA_JV_SUCCESS;
evt_data.rfc_srv_open.rem_bda = rem_bda;
@@ -1521,7 +1526,11 @@
tBTA_JV_RFC_CB* p_cb = bta_jv_rfc_port_to_cb(port_handle);
tBTA_JV evt_data;
- if (NULL == p_cb || NULL == p_cb->p_cback) return;
+ if (NULL == p_cb || NULL == p_cb->p_cback) {
+ LOG(ERROR) << __func__ << ": p_cb=" << p_cb
+ << ", p_cb->p_cback=" << (p_cb ? p_cb->p_cback : 0);
+ return;
+ }
VLOG(2) << __func__ << ": code=" << loghex(code)
<< ", port_handle=" << port_handle << ", handle=" << p_cb->handle;
diff --git a/btif/src/btif_hf.cc b/btif/src/btif_hf.cc
index 5573160..a3306ec 100644
--- a/btif/src/btif_hf.cc
+++ b/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 @@
<< 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/stack/Android.bp b/stack/Android.bp
index 2b5d430..76cb172 100644
--- a/stack/Android.bp
+++ b/stack/Android.bp
@@ -197,7 +197,9 @@
"system/bt",
"system/bt/internal_include",
],
- srcs: ["test/stack_a2dp_test.cc"],
+ srcs: [
+ "test/stack_a2dp_test.cc",
+ ],
shared_libs: [
"libhidlbase",
"liblog",
@@ -221,6 +223,56 @@
],
}
+cc_test {
+ name: "net_test_stack_rfcomm",
+ defaults: ["fluoride_defaults"],
+ host_supported: true,
+ local_include_dirs: [
+ "include",
+ "btm",
+ "l2cap",
+ "smp",
+ "rfcomm",
+ "test/common",
+ ],
+ include_dirs: [
+ "system/bt",
+ "system/bt/internal_include",
+ "system/bt/btcore/include",
+ "system/bt/hci/include",
+ "system/bt/utils/include",
+ ],
+ srcs: [
+ "rfcomm/port_api.cc",
+ "rfcomm/port_rfc.cc",
+ "rfcomm/port_utils.cc",
+ "rfcomm/rfc_l2cap_if.cc",
+ "rfcomm/rfc_mx_fsm.cc",
+ "rfcomm/rfc_port_fsm.cc",
+ "rfcomm/rfc_port_if.cc",
+ "rfcomm/rfc_ts_frames.cc",
+ "rfcomm/rfc_utils.cc",
+ "test/common/mock_btm_layer.cc",
+ "test/common/mock_btu_layer.cc",
+ "test/common/mock_l2cap_layer.cc",
+ "test/common/stack_test_packet_utils.cc",
+ "test/rfcomm/stack_rfcomm_test.cc",
+ "test/rfcomm/stack_rfcomm_test_main.cc",
+ "test/rfcomm/stack_rfcomm_test_utils.cc",
+ "test/rfcomm/stack_rfcomm_test_utils_test.cc",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libprotobuf-cpp-lite",
+ ],
+ static_libs: [
+ "liblog",
+ "libgmock",
+ "libosi",
+ "libbt-protos-lite",
+ ],
+}
+
// Bluetooth stack smp unit tests for target
// ========================================================
cc_test {
diff --git a/stack/include/port_api.h b/stack/include/port_api.h
index aedbe74..b0a7e63 100644
--- a/stack/include/port_api.h
+++ b/stack/include/port_api.h
@@ -309,7 +309,7 @@
* p_lcid - OUT L2CAP's LCID
*
******************************************************************************/
-extern int PORT_CheckConnection(uint16_t handle, RawAddress& bd_addr,
+extern int PORT_CheckConnection(uint16_t handle, RawAddress* bd_addr,
uint16_t* p_lcid);
/*******************************************************************************
@@ -323,7 +323,7 @@
* bd_addr - bd_addr of the peer
*
******************************************************************************/
-extern bool PORT_IsOpening(RawAddress& bd_addr);
+extern bool PORT_IsOpening(RawAddress* bd_addr);
/*******************************************************************************
*
diff --git a/stack/l2cap/l2c_api.cc b/stack/l2cap/l2c_api.cc
index 951a45b..173d0a6 100644
--- a/stack/l2cap/l2c_api.cc
+++ b/stack/l2cap/l2c_api.cc
@@ -275,7 +275,7 @@
*
******************************************************************************/
uint16_t L2CA_ConnectReq(uint16_t psm, const RawAddress& p_bd_addr) {
- return L2CA_ErtmConnectReq(psm, p_bd_addr, NULL);
+ return L2CA_ErtmConnectReq(psm, p_bd_addr, nullptr);
}
/*******************************************************************************
@@ -297,10 +297,6 @@
******************************************************************************/
uint16_t L2CA_ErtmConnectReq(uint16_t psm, const RawAddress& p_bd_addr,
tL2CAP_ERTM_INFO* p_ertm_info) {
- tL2C_LCB* p_lcb;
- tL2C_CCB* p_ccb;
- tL2C_RCB* p_rcb;
-
VLOG(1) << __func__ << "BDA " << p_bd_addr
<< StringPrintf(" PSM: 0x%04x allowed:0x%x preferred:%d", psm,
(p_ertm_info) ? p_ertm_info->allowed_modes : 0,
@@ -308,36 +304,36 @@
/* Fail if we have not established communications with the controller */
if (!BTM_IsDeviceUp()) {
- L2CAP_TRACE_WARNING("L2CAP connect req - BTU not ready");
- return (0);
+ LOG(WARNING) << __func__ << ": BTU not ready";
+ return 0;
}
/* Fail if the PSM is not registered */
- p_rcb = l2cu_find_rcb_by_psm(psm);
- if (p_rcb == NULL) {
- L2CAP_TRACE_WARNING("L2CAP - no RCB for L2CA_conn_req, PSM: 0x%04x", psm);
- return (0);
+ tL2C_RCB* p_rcb = l2cu_find_rcb_by_psm(psm);
+ if (p_rcb == nullptr) {
+ LOG(WARNING) << __func__ << ": no RCB, PSM=" << loghex(psm);
+ return 0;
}
/* First, see if we already have a link to the remote */
/* assume all ERTM l2cap connection is going over BR/EDR for now */
- p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_BR_EDR);
- if (p_lcb == NULL) {
+ tL2C_LCB* p_lcb = l2cu_find_lcb_by_bd_addr(p_bd_addr, BT_TRANSPORT_BR_EDR);
+ if (p_lcb == nullptr) {
/* No link. Get an LCB and start link establishment */
p_lcb = l2cu_allocate_lcb(p_bd_addr, false, BT_TRANSPORT_BR_EDR);
/* currently use BR/EDR for ERTM mode l2cap connection */
- if ((p_lcb == NULL) || (!l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR))) {
- L2CAP_TRACE_WARNING(
- "L2CAP - conn not started for PSM: 0x%04x p_lcb: 0x%08x", psm,
- p_lcb);
- return (0);
+ if ((p_lcb == nullptr) || (!l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR))) {
+ LOG(WARNING) << __func__
+ << ": connection not started for PSM=" << loghex(psm)
+ << ", p_lcb=" << p_lcb;
+ return 0;
}
}
/* Allocate a channel control block */
- p_ccb = l2cu_allocate_ccb(p_lcb, 0);
- if (p_ccb == NULL) {
- L2CAP_TRACE_WARNING("L2CAP - no CCB for L2CA_conn_req, PSM: 0x%04x", psm);
- return (0);
+ tL2C_CCB* p_ccb = l2cu_allocate_ccb(p_lcb, 0);
+ if (p_ccb == nullptr) {
+ LOG(WARNING) << __func__ << ": no CCB, PSM=" << loghex(psm);
+ return 0;
}
/* Save registration info */
@@ -366,16 +362,14 @@
/* If link is up, start the L2CAP connection */
if (p_lcb->link_state == LST_CONNECTED) {
- l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_REQ, NULL);
- }
-
- /* If link is disconnecting, save link info to retry after disconnect
- * Possible Race condition when a reconnect occurs
- * on the channel during a disconnect of link. This
- * ccb will be automatically retried after link disconnect
- * arrives
- */
- else if (p_lcb->link_state == LST_DISCONNECTING) {
+ l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONNECT_REQ, nullptr);
+ } else if (p_lcb->link_state == LST_DISCONNECTING) {
+ /* If link is disconnecting, save link info to retry after disconnect
+ * Possible Race condition when a reconnect occurs
+ * on the channel during a disconnect of link. This
+ * ccb will be automatically retried after link disconnect
+ * arrives
+ */
L2CAP_TRACE_DEBUG("L2CAP API - link disconnecting: RETRY LATER");
/* Save ccb so it can be started after disconnect is finished */
@@ -386,7 +380,7 @@
psm, p_ccb->local_cid);
/* Return the local CID as our handle */
- return (p_ccb->local_cid);
+ return p_ccb->local_cid;
}
/*******************************************************************************
diff --git a/stack/rfcomm/port_api.cc b/stack/rfcomm/port_api.cc
index 1ae8667..0bb817d 100644
--- a/stack/rfcomm/port_api.cc
+++ b/stack/rfcomm/port_api.cc
@@ -118,7 +118,7 @@
*p_handle = 0;
if ((scn == 0) || (scn >= PORT_MAX_RFC_PORTS)) {
- /* Server Channel Number(SCN) should be in range 1...30 */
+ // Server Channel Number (SCN) should be in range [1, 30]
LOG(ERROR) << __func__ << ": Invalid SCN, bd_addr=" << bd_addr
<< ", scn=" << static_cast<int>(scn)
<< ", is_server=" << is_server
@@ -127,8 +127,8 @@
return (PORT_INVALID_SCN);
}
- /* For client that originate connection on the existing none initiator */
- /* multiplexer channel DLCI should be odd */
+ // For client that originates connection on the existing none initiator
+ // multiplexer channel, DLCI should be odd.
uint8_t dlci;
tRFC_MCB* p_mcb = port_find_mcb(bd_addr);
if (p_mcb && !p_mcb->is_initiator && !is_server) {
@@ -137,13 +137,13 @@
dlci = (scn << 1);
}
- /* For the server side always allocate a new port. On the client side */
- /* do not allow the same (dlci, bd_addr) to be opened twice by application */
+ // On the client side, do not allow the same (dlci, bd_addr) to be opened
+ // twice by application
tPORT* p_port;
if (!is_server) {
p_port = port_find_port(dlci, bd_addr);
if (p_port != nullptr) {
- /* if existing port is also a client port */
+ // if existing port is also a client port, error out
if (!p_port->is_server) {
LOG(ERROR) << __func__ << ": already at opened state "
<< static_cast<int>(p_port->state)
@@ -153,22 +153,26 @@
<< ", bd_addr=" << bd_addr << ", scn=" << std::to_string(scn)
<< ", is_server=" << is_server << ", mtu=" << mtu
<< ", uuid=" << loghex(uuid) << ", dlci=" << +dlci
- << ", p_mcb=" << p_mcb << ", port=" << +p_port->inx;
- *p_handle = p_port->inx;
+ << ", p_mcb=" << p_mcb
+ << ", port=" << std::to_string(p_port->handle);
+ *p_handle = p_port->handle;
return (PORT_ALREADY_OPENED);
}
}
}
+ // On the server side, always allocate a new port.
p_port = port_allocate_port(dlci, bd_addr);
if (p_port == nullptr) {
LOG(ERROR) << __func__ << ": no resources, bd_addr=" << bd_addr
<< ", scn=" << std::to_string(scn) << ", is_server=" << is_server
<< ", mtu=" << mtu << ", uuid=" << loghex(uuid)
<< ", dlci=" << +dlci;
- return (PORT_NO_RESOURCES);
+ return PORT_NO_RESOURCES;
}
+ *p_handle = p_port->handle;
+ // Get default signal state
switch (uuid) {
case UUID_PROTOCOL_OBEX:
p_port->default_signal_state = PORT_OBEX_DEFAULT_SIGNAL_STATE;
@@ -189,42 +193,35 @@
break;
}
- *p_handle = p_port->inx;
-
+ // Assign port specific values
p_port->state = PORT_STATE_OPENING;
p_port->uuid = uuid;
p_port->is_server = is_server;
p_port->scn = scn;
p_port->ev_mask = 0;
- /* If the MTU is not specified (0), keep MTU decision until the
- * PN frame has to be send
- * at that time connection should be established and we
- * will know for sure our prefered MTU
- */
-
+ // Find MTU
+ // If the MTU is not specified (0), keep MTU decision until the PN frame has
+ // to be send at that time connection should be established and we will know
+ // for sure our prefered MTU
uint16_t rfcomm_mtu = L2CAP_MTU_SIZE - RFCOMM_DATA_OVERHEAD;
-
if (mtu) {
p_port->mtu = (mtu < rfcomm_mtu) ? mtu : rfcomm_mtu;
} else {
p_port->mtu = rfcomm_mtu;
}
- /* server doesn't need to release port when closing */
+ // Other states
+ // server doesn't need to release port when closing
if (is_server) {
p_port->keep_port_handle = true;
-
- /* keep mtu that user asked, p_port->mtu could be updated during param
- * negotiation */
+ // keep mtu that user asked, p_port->mtu could be updated during param
+ // negotiation
p_port->keep_mtu = p_port->mtu;
}
-
p_port->local_ctrl.modem_signal = p_port->default_signal_state;
p_port->local_ctrl.fc = false;
-
p_port->p_mgmt_callback = p_mgmt_cb;
-
p_port->bd_addr = bd_addr;
LOG(INFO) << __func__ << ": bd_addr=" << bd_addr
@@ -234,12 +231,12 @@
<< ", signal_state=" << loghex(p_port->default_signal_state)
<< ", p_port=" << p_port;
- /* If this is not initiator of the connection need to just wait */
+ // If this is not initiator of the connection need to just wait
if (p_port->is_server) {
return (PORT_SUCCESS);
}
- /* Open will be continued after security checks are passed */
+ // Open will be continued after security checks are passed
return port_open_continue(p_port);
}
@@ -286,24 +283,21 @@
*
******************************************************************************/
int RFCOMM_RemoveServer(uint16_t handle) {
- tPORT* p_port;
-
- RFCOMM_TRACE_API("RFCOMM_RemoveServer() handle:%d", handle);
-
/* Check if handle is valid to avoid crashing */
if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
- RFCOMM_TRACE_ERROR("RFCOMM_RemoveServer() BAD handle:%d", handle);
+ LOG(ERROR) << __func__ << ": bad handle " << handle;
return (PORT_BAD_HANDLE);
}
- p_port = &rfc_cb.port.port[handle - 1];
+ tPORT* p_port = &rfc_cb.port.port[handle - 1];
/* Do not report any events to the client any more. */
- p_port->p_mgmt_callback = NULL;
+ p_port->p_mgmt_callback = nullptr;
if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
- RFCOMM_TRACE_EVENT("RFCOMM_RemoveServer() Not opened:%d", handle);
+ VLOG(1) << __func__ << ": handle " << handle << " not opened";
return (PORT_SUCCESS);
}
+ LOG(INFO) << __func__ << ": handle=" << handle;
/* this port will be deallocated after closing */
p_port->keep_port_handle = false;
@@ -486,18 +480,19 @@
* p_lcid - OUT L2CAP's LCID
*
******************************************************************************/
-int PORT_CheckConnection(uint16_t handle, RawAddress& bd_addr,
+int PORT_CheckConnection(uint16_t handle, RawAddress* bd_addr,
uint16_t* p_lcid) {
- tPORT* p_port;
-
- RFCOMM_TRACE_API("PORT_CheckConnection() handle:%d", handle);
-
/* Check if handle is valid to avoid crashing */
if ((handle == 0) || (handle > MAX_RFC_PORTS)) {
return (PORT_BAD_HANDLE);
}
-
- p_port = &rfc_cb.port.port[handle - 1];
+ tPORT* p_port = &rfc_cb.port.port[handle - 1];
+ RFCOMM_TRACE_DEBUG(
+ "%s: handle=%d, in_use=%d, port_state=%d, p_mcb=%p, peer_ready=%d, "
+ "rfc_state=%d",
+ __func__, handle, p_port->in_use, p_port->state, p_port->rfc.p_mcb,
+ (p_port->rfc.p_mcb ? p_port->rfc.p_mcb->peer_ready : -1),
+ p_port->rfc.state);
if (!p_port->in_use || (p_port->state == PORT_STATE_CLOSED)) {
return (PORT_NOT_OPENED);
@@ -508,7 +503,7 @@
return (PORT_LINE_ERR);
}
- bd_addr = p_port->rfc.p_mcb->bd_addr;
+ *bd_addr = p_port->rfc.p_mcb->bd_addr;
if (p_lcid) *p_lcid = p_port->rfc.p_mcb->lcid;
return (PORT_SUCCESS);
@@ -525,28 +520,23 @@
* bd_addr - bd_addr of the peer
*
******************************************************************************/
-bool PORT_IsOpening(RawAddress& bd_addr) {
- uint8_t xx, yy;
- tRFC_MCB* p_mcb = NULL;
- tPORT* p_port;
- bool found_port;
-
+bool PORT_IsOpening(RawAddress* bd_addr) {
/* Check for any rfc_mcb which is in the middle of opening. */
- for (xx = 0; xx < MAX_BD_CONNECTIONS; xx++) {
- if ((rfc_cb.port.rfc_mcb[xx].state > RFC_MX_STATE_IDLE) &&
- (rfc_cb.port.rfc_mcb[xx].state < RFC_MX_STATE_CONNECTED)) {
- bd_addr = rfc_cb.port.rfc_mcb[xx].bd_addr;
+ for (auto& multiplexer_cb : rfc_cb.port.rfc_mcb) {
+ if ((multiplexer_cb.state > RFC_MX_STATE_IDLE) &&
+ (multiplexer_cb.state < RFC_MX_STATE_CONNECTED)) {
+ *bd_addr = multiplexer_cb.bd_addr;
return true;
}
- if (rfc_cb.port.rfc_mcb[xx].state == RFC_MX_STATE_CONNECTED) {
- found_port = false;
- p_mcb = &rfc_cb.port.rfc_mcb[xx];
- p_port = &rfc_cb.port.port[0];
+ if (multiplexer_cb.state == RFC_MX_STATE_CONNECTED) {
+ bool found_port = false;
+ tPORT* p_port = nullptr;
- for (yy = 0; yy < MAX_RFC_PORTS; yy++, p_port++) {
- if (p_port->rfc.p_mcb == p_mcb) {
+ for (tPORT& port : rfc_cb.port.port) {
+ if (port.rfc.p_mcb == &multiplexer_cb) {
found_port = true;
+ p_port = &port;
break;
}
}
@@ -554,7 +544,7 @@
if ((!found_port) ||
(found_port && (p_port->rfc.state < RFC_STATE_OPENED))) {
/* Port is not established yet. */
- bd_addr = rfc_cb.port.rfc_mcb[xx].bd_addr;
+ *bd_addr = multiplexer_cb.bd_addr;
return true;
}
}
@@ -827,7 +817,7 @@
events &= p_port->ev_mask;
if (p_port->p_callback && events) {
- p_port->p_callback(events, p_port->inx);
+ p_port->p_callback(events, p_port->handle);
}
}
return (PORT_SUCCESS);
@@ -894,7 +884,7 @@
events &= p_port->ev_mask;
if (p_port->p_callback && events) {
- p_port->p_callback(events, p_port->inx);
+ p_port->p_callback(events, p_port->handle);
}
}
return (PORT_SUCCESS);
@@ -1118,7 +1108,7 @@
events &= p_port->ev_mask;
if ((p_port->p_callback != NULL) && events)
- (p_port->p_callback)(events, p_port->inx);
+ (p_port->p_callback)(events, p_port->handle);
}
return (PORT_SUCCESS);
@@ -1306,7 +1296,7 @@
osi_free(p_buf);
if ((p_port->p_callback != NULL) && (p_port->ev_mask & PORT_EV_ERR))
- p_port->p_callback(PORT_EV_ERR, p_port->inx);
+ p_port->p_callback(PORT_EV_ERR, p_port->handle);
return (PORT_TX_FULL);
}
@@ -1384,7 +1374,7 @@
event &= p_port->ev_mask;
/* Send event to the application */
- if (p_port->p_callback && event) (p_port->p_callback)(event, p_port->inx);
+ if (p_port->p_callback && event) (p_port->p_callback)(event, p_port->handle);
return (PORT_SUCCESS);
}
@@ -1539,7 +1529,7 @@
event &= p_port->ev_mask;
/* Send event to the application */
- if (p_port->p_callback && event) (p_port->p_callback)(event, p_port->inx);
+ if (p_port->p_callback && event) (p_port->p_callback)(event, p_port->handle);
return (PORT_SUCCESS);
}
@@ -1650,7 +1640,7 @@
event &= p_port->ev_mask;
/* Send event to the application */
- if (p_port->p_callback && event) (p_port->p_callback)(event, p_port->inx);
+ if (p_port->p_callback && event) (p_port->p_callback)(event, p_port->handle);
return (PORT_SUCCESS);
}
diff --git a/stack/rfcomm/port_int.h b/stack/rfcomm/port_int.h
index db7d0fd..ecd8fbc 100644
--- a/stack/rfcomm/port_int.h
+++ b/stack/rfcomm/port_int.h
@@ -90,8 +90,8 @@
typedef struct {
alarm_t* mcb_timer; /* MCB timer */
fixed_queue_t* cmd_q; /* Queue for command messages on this mux */
- uint8_t port_inx[RFCOMM_MAX_DLCI + 1]; /* Array for quick access to */
- /* tPORT based on dlci */
+ uint8_t port_handles[RFCOMM_MAX_DLCI + 1]; /* Array for quick access to */
+ /* port handles based on dlci */
RawAddress bd_addr; /* BD ADDR of the peer if initiator */
uint16_t lcid; /* Local cid used for this channel */
uint16_t peer_l2cap_mtu; /* Max frame that can be sent to peer L2CAP */
@@ -139,7 +139,7 @@
* Define control block containing information about PORT connection
*/
typedef struct {
- uint8_t inx; /* Index of this control block in the port_info array */
+ uint8_t handle; // Starting from 1, unique for this object
bool in_use; /* True when structure is allocated */
#define PORT_STATE_CLOSED 0
diff --git a/stack/rfcomm/port_rfc.cc b/stack/rfcomm/port_rfc.cc
index 5ec5631..3228f64 100644
--- a/stack/rfcomm/port_rfc.cc
+++ b/stack/rfcomm/port_rfc.cc
@@ -56,13 +56,11 @@
*
******************************************************************************/
int port_open_continue(tPORT* p_port) {
- tRFC_MCB* p_mcb;
-
RFCOMM_TRACE_EVENT("port_open_continue, p_port:%p", p_port);
/* Check if multiplexer channel has already been established */
- p_mcb = rfc_alloc_multiplexer_channel(p_port->bd_addr, true);
- if (p_mcb == NULL) {
+ tRFC_MCB* p_mcb = rfc_alloc_multiplexer_channel(p_port->bd_addr, true);
+ if (p_mcb == nullptr) {
RFCOMM_TRACE_WARNING("port_open_continue no mx channel");
port_release_port(p_port);
return (PORT_NO_RESOURCES);
@@ -70,24 +68,22 @@
p_port->rfc.p_mcb = p_mcb;
- p_mcb->port_inx[p_port->dlci] = p_port->inx;
+ p_mcb->port_handles[p_port->dlci] = p_port->handle;
/* Connection is up and we know local and remote features, select MTU */
port_select_mtu(p_port);
if (p_mcb->state == RFC_MX_STATE_CONNECTED) {
- RFCOMM_ParNegReq(p_mcb, p_port->dlci, p_port->mtu);
+ RFCOMM_ParameterNegotiationRequest(p_mcb, p_port->dlci, p_port->mtu);
} else if ((p_mcb->state == RFC_MX_STATE_IDLE) ||
(p_mcb->state == RFC_MX_STATE_DISC_WAIT_UA)) {
- /* In RFC_MX_STATE_IDLE state, MX state machine will create connection */
- /* In RFC_MX_STATE_DISC_WAIT_UA state, MX state machine will recreate
- * connection */
- /* after disconnecting is completed */
+ // In RFC_MX_STATE_IDLE state, MX state machine will create connection
+ // In RFC_MX_STATE_DISC_WAIT_UA state, MX state machine will recreate
+ // connection after disconnecting is completed
RFCOMM_StartReq(p_mcb);
} else {
- /* MX state machine ignores RFC_MX_EVENT_START_REQ in these states */
- /* When it enters RFC_MX_STATE_CONNECTED, it will check any openning ports
- */
+ // MX state machine ignores RFC_MX_EVENT_START_REQ in these states
+ // When it enters RFC_MX_STATE_CONNECTED, it will check any openning ports
RFCOMM_TRACE_DEBUG(
"port_open_continue: mx state(%d) mx channel is openning",
p_mcb->state);
@@ -128,7 +124,8 @@
if (p_mcb == NULL) return;
- RFCOMM_PortNegReq(p_mcb, p_port->dlci, &p_port->user_port_pars);
+ RFCOMM_PortParameterNegotiationRequest(p_mcb, p_port->dlci,
+ &p_port->user_port_pars);
}
/*******************************************************************************
@@ -162,14 +159,14 @@
if (p_port->ev_mask & PORT_EV_ERR) events |= PORT_EV_ERR;
if ((p_port->p_callback != NULL) && events)
- p_port->p_callback(events, p_port->inx);
+ p_port->p_callback(events, p_port->handle);
/* Check if RFCOMM side has been closed while the message was queued */
if ((p_mcb == NULL) || (p_port->rfc.state == RFC_STATE_CLOSED)) {
/* Call management callback function before calling port_release_port() to
* clear tPort */
if (p_port->p_mgmt_callback)
- p_port->p_mgmt_callback(PORT_CLOSED, p_port->inx);
+ p_port->p_mgmt_callback(PORT_CLOSED, p_port->handle);
port_release_port(p_port);
} else {
@@ -188,37 +185,39 @@
*
******************************************************************************/
void PORT_StartCnf(tRFC_MCB* p_mcb, uint16_t result) {
- tPORT* p_port;
- int i;
bool no_ports_up = true;
- RFCOMM_TRACE_EVENT("PORT_StartCnf result:%d", result);
+ RFCOMM_TRACE_EVENT("%s: result %d", __func__, result);
- p_port = &rfc_cb.port.port[0];
- for (i = 0; i < MAX_RFC_PORTS; i++, p_port++) {
+ tPORT* p_port = &rfc_cb.port.port[0];
+ for (int i = 0; i < MAX_RFC_PORTS; i++, p_port++) {
if (p_port->rfc.p_mcb == p_mcb) {
no_ports_up = false;
- if (result == RFCOMM_SUCCESS)
- RFCOMM_ParNegReq(p_mcb, p_port->dlci, p_port->mtu);
- else {
- RFCOMM_TRACE_WARNING("PORT_StartCnf failed result:%d", result);
+ if (result == RFCOMM_SUCCESS) {
+ RFCOMM_TRACE_EVENT("%s: dlci %d", __func__, p_port->dlci);
+ RFCOMM_ParameterNegotiationRequest(p_mcb, p_port->dlci, p_port->mtu);
+ } else {
+ RFCOMM_TRACE_WARNING("%s: failed result:%d", __func__, result);
/* Warning: result is also set to 4 when l2cap connection
fails due to l2cap connect cnf (no_resources) */
- if (result == HCI_ERR_PAGE_TIMEOUT)
+ if (result == HCI_ERR_PAGE_TIMEOUT) {
p_port->error = PORT_PAGE_TIMEOUT;
- else
+ } else {
p_port->error = PORT_START_FAILED;
+ }
rfc_release_multiplexer_channel(p_mcb);
/* Send event to the application */
- if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECT_ERR))
- (p_port->p_callback)(PORT_EV_CONNECT_ERR, p_port->inx);
+ if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECT_ERR)) {
+ (p_port->p_callback)(PORT_EV_CONNECT_ERR, p_port->handle);
+ }
- if (p_port->p_mgmt_callback)
- p_port->p_mgmt_callback(PORT_START_FAILED, p_port->inx);
+ if (p_port->p_mgmt_callback) {
+ p_port->p_mgmt_callback(PORT_START_FAILED, p_port->handle);
+ }
port_release_port(p_port);
}
@@ -272,12 +271,9 @@
******************************************************************************/
void PORT_ParNegInd(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu, uint8_t cl,
uint8_t k) {
+ RFCOMM_TRACE_EVENT("%s: bd_addr=%s, dlci=%d, mtu=%d", __func__,
+ p_mcb->bd_addr.ToString().c_str(), dlci, mtu);
tPORT* p_port = port_find_mcb_dlci_port(p_mcb, dlci);
- uint8_t our_cl;
- uint8_t our_k;
-
- RFCOMM_TRACE_EVENT("PORT_ParNegInd dlci:%d mtu:%d", dlci, mtu);
-
if (!p_port) {
/* This can be a first request for this port */
p_port = port_find_dlci_port(dlci);
@@ -292,9 +288,9 @@
rfc_check_mcb_active(p_mcb);
return;
}
- RFCOMM_TRACE_EVENT("%s: port_inx[dlci:%d]:%d->%d", __func__, dlci,
- p_mcb->port_inx[dlci], p_port->inx);
- p_mcb->port_inx[dlci] = p_port->inx;
+ RFCOMM_TRACE_EVENT("%s: port_handles[dlci:%d]:%d->%d", __func__, dlci,
+ p_mcb->port_handles[dlci], p_port->handle);
+ p_mcb->port_handles[dlci] = p_port->handle;
}
p_port->bd_addr = p_mcb->bd_addr;
@@ -329,6 +325,8 @@
/* after the DLCI is already established-- the PN in that case must have cl =
* 0. */
/* See RFCOMM spec 5.5.3 */
+ uint8_t our_cl;
+ uint8_t our_k;
if (cl == RFCOMM_PN_CONV_LAYER_TYPE_1) {
our_cl = RFCOMM_PN_CONV_LAYER_TYPE_1;
our_k = 0;
@@ -346,7 +344,7 @@
our_cl = RFCOMM_PN_CONV_LAYER_TYPE_1;
our_k = 0;
}
- RFCOMM_ParNegRsp(p_mcb, dlci, p_port->mtu, our_cl, our_k);
+ RFCOMM_ParameterNegotiationResponse(p_mcb, dlci, p_port->mtu, our_cl, our_k);
}
/*******************************************************************************
@@ -362,12 +360,13 @@
******************************************************************************/
void PORT_ParNegCnf(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu, uint8_t cl,
uint8_t k) {
- tPORT* p_port = port_find_mcb_dlci_port(p_mcb, dlci);
-
RFCOMM_TRACE_EVENT("PORT_ParNegCnf dlci:%d mtu:%d cl: %d k: %d", dlci, mtu,
cl, k);
-
- if (!p_port) return;
+ tPORT* p_port = port_find_mcb_dlci_port(p_mcb, dlci);
+ if (!p_port) {
+ LOG(WARNING) << __func__ << ": port is null for " << p_mcb->bd_addr;
+ return;
+ }
/* Flow control mechanism not set yet. Negotiate flow control mechanism. */
if (p_mcb->flow == PORT_FC_UNDEFINED) {
@@ -377,19 +376,17 @@
if ((PORT_FC_DEFAULT == PORT_FC_TS710) &&
(cl == RFCOMM_PN_CONV_LAYER_CBFC_R)) {
RFCOMM_TRACE_WARNING("%s, negotiation fails, index=%d", __func__,
- p_port->inx);
+ p_port->handle);
rfc_send_disc(p_mcb, p_port->dlci);
rfc_port_closed(p_port);
return;
- }
- /* Our stack is configured for credit-based and they responded with
- credit-based. */
- else if (cl == RFCOMM_PN_CONV_LAYER_CBFC_R) {
+ } else if (cl == RFCOMM_PN_CONV_LAYER_CBFC_R) {
+ // Our stack is configured for credit-based and they responded with
+ // credit-based.
p_mcb->flow = PORT_FC_CREDIT;
- }
- /* They responded with any other value. Treat this as negotiation to
- TS07.10. */
- else {
+ } else {
+ // They responded with any other value. Treat this as negotiation to
+ // TS07.10.
p_mcb->flow = PORT_FC_TS710;
}
}
@@ -436,7 +433,7 @@
RFCOMM_DlcEstablishRsp(p_mcb, dlci, 0, RFCOMM_ERROR);
return;
}
- p_mcb->port_inx[dlci] = p_port->inx;
+ p_mcb->port_handles[dlci] = p_port->handle;
}
/* If L2CAP's mtu less then RFCOMM's take it */
@@ -450,10 +447,10 @@
/* This is the server side. If application wants to know when connection */
/* is established, thats the place */
if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECTED))
- (p_port->p_callback)(PORT_EV_CONNECTED, p_port->inx);
+ (p_port->p_callback)(PORT_EV_CONNECTED, p_port->handle);
if (p_port->p_mgmt_callback)
- p_port->p_mgmt_callback(PORT_SUCCESS, p_port->inx);
+ p_port->p_mgmt_callback(PORT_SUCCESS, p_port->handle);
p_port->state = PORT_STATE_OPENED;
}
@@ -490,10 +487,10 @@
rfc_timer_stop(p_mcb);
if (p_port->p_callback && (p_port->ev_mask & PORT_EV_CONNECTED))
- (p_port->p_callback)(PORT_EV_CONNECTED, p_port->inx);
+ (p_port->p_callback)(PORT_EV_CONNECTED, p_port->handle);
if (p_port->p_mgmt_callback)
- p_port->p_mgmt_callback(PORT_SUCCESS, p_port->inx);
+ p_port->p_mgmt_callback(PORT_SUCCESS, p_port->handle);
p_port->state = PORT_STATE_OPENED;
@@ -501,7 +498,8 @@
*/
if ((p_port->uuid == UUID_SERVCLASS_DIALUP_NETWORKING) ||
(p_port->uuid == UUID_SERVCLASS_FAX))
- RFCOMM_PortNegReq(p_port->rfc.p_mcb, p_port->dlci, NULL);
+ RFCOMM_PortParameterNegotiationRequest(p_port->rfc.p_mcb, p_port->dlci,
+ NULL);
else
RFCOMM_ControlReq(p_port->rfc.p_mcb, p_port->dlci, &p_port->local_ctrl);
}
@@ -527,15 +525,15 @@
/* This can be a first request for this port */
p_port = port_find_dlci_port(dlci);
if (!p_port) {
- RFCOMM_PortNegRsp(p_mcb, dlci, p_pars, 0);
+ RFCOMM_PortParameterNegotiationResponse(p_mcb, dlci, p_pars, 0);
return;
}
- p_mcb->port_inx[dlci] = p_port->inx;
+ p_mcb->port_handles[dlci] = p_port->handle;
}
/* Check if the flow control is acceptable on local side */
p_port->peer_port_pars = *p_pars;
- RFCOMM_PortNegRsp(p_mcb, dlci, p_pars, param_mask);
+ RFCOMM_PortParameterNegotiationResponse(p_mcb, dlci, p_pars, param_mask);
}
/*******************************************************************************
@@ -596,9 +594,9 @@
p_port->peer_ctrl = *p_pars;
- if (!(p_port->port_ctrl & PORT_CTRL_REQ_SENT))
+ if (!(p_port->port_ctrl & PORT_CTRL_REQ_SENT)) {
RFCOMM_ControlReq(p_port->rfc.p_mcb, p_port->dlci, &p_port->local_ctrl);
- else {
+ } else {
/* If this is the first time we received control RFCOMM is connected */
if (!(p_port->port_ctrl & PORT_CTRL_IND_RECEIVED)) {
event |= (PORT_EV_CONNECTED & p_port->ev_mask);
@@ -615,7 +613,7 @@
/* execute call back function only if the application is registered for events
*/
- if (event && p_port->p_callback) (p_port->p_callback)(event, p_port->inx);
+ if (event && p_port->p_callback) (p_port->p_callback)(event, p_port->handle);
RFCOMM_TRACE_EVENT(
"PORT_ControlInd DTR_DSR : %d, RTS_CTS : %d, RI : %d, DCD : %d",
@@ -655,7 +653,7 @@
/* execute call back function only if the application is registered for events
*/
- if (event && p_port->p_callback) (p_port->p_callback)(event, p_port->inx);
+ if (event && p_port->p_callback) (p_port->p_callback)(event, p_port->handle);
}
/*******************************************************************************
@@ -683,7 +681,7 @@
if (line_status & ~(PORT_ERR_OVERRUN | PORT_ERR_BREAK)) event |= PORT_EV_ERR;
if ((p_port->p_callback != NULL) && (p_port->ev_mask & event))
- p_port->p_callback((p_port->ev_mask & event), p_port->inx);
+ p_port->p_callback((p_port->ev_mask & event), p_port->handle);
}
/*******************************************************************************
@@ -695,12 +693,10 @@
*
******************************************************************************/
void PORT_DlcReleaseInd(tRFC_MCB* p_mcb, uint8_t dlci) {
+ VLOG(1) << __func__ << ": dlci=" << std::to_string(dlci)
+ << ", bd_addr=" << p_mcb->bd_addr;
tPORT* p_port = port_find_mcb_dlci_port(p_mcb, dlci);
-
- RFCOMM_TRACE_EVENT("PORT_DlcReleaseInd");
-
if (!p_port) return;
-
port_rfc_closed(p_port, PORT_CLOSED);
}
@@ -775,7 +771,7 @@
* receive data */
if (p_port->p_data_co_callback) {
/* Another packet is delivered to user. Send credits to peer if required */
- if (p_port->p_data_co_callback(p_port->inx, (uint8_t*)p_buf, -1,
+ if (p_port->p_data_co_callback(p_port->handle, (uint8_t*)p_buf, -1,
DATA_CO_CALLBACK_TYPE_INCOMING)) {
port_flow_control_peer(p_port, true, 1);
} else {
@@ -788,8 +784,8 @@
if (p_port->p_data_callback) {
/* Another packet is delivered to user. Send credits to peer if required */
port_flow_control_peer(p_port, true, 1);
- p_port->p_data_callback(p_port->inx, (uint8_t*)(p_buf + 1) + p_buf->offset,
- p_buf->len);
+ p_port->p_data_callback(p_port->handle,
+ (uint8_t*)(p_buf + 1) + p_buf->offset, p_buf->len);
osi_free(p_buf);
return;
}
@@ -837,7 +833,7 @@
/* Mask out all events that are not of interest to user */
events &= p_port->ev_mask;
- if (p_port->p_callback && events) p_port->p_callback(events, p_port->inx);
+ if (p_port->p_callback && events) p_port->p_callback(events, p_port->handle);
}
/*******************************************************************************
@@ -884,7 +880,8 @@
events &= p_port->ev_mask;
/* Send event to the application */
- if (p_port->p_callback && events) (p_port->p_callback)(events, p_port->inx);
+ if (p_port->p_callback && events)
+ (p_port->p_callback)(events, p_port->handle);
/* If DLCI is not 0 event applies to one port only */
if (dlci != 0) break;
@@ -962,7 +959,7 @@
p_port->rfc.state = RFC_STATE_CLOSED;
if (p_mcb) {
- p_mcb->port_inx[p_port->dlci] = 0;
+ p_mcb->port_handles[p_port->dlci] = 0;
/* If there are no more ports opened on this MCB release it */
rfc_check_mcb_active(p_mcb);
@@ -993,17 +990,19 @@
}
if ((p_port->p_callback != NULL) && events)
- p_port->p_callback(events, p_port->inx);
+ p_port->p_callback(events, p_port->handle);
- if (p_port->p_mgmt_callback) p_port->p_mgmt_callback(res, p_port->inx);
+ if (p_port->p_mgmt_callback) p_port->p_mgmt_callback(res, p_port->handle);
p_port->rfc.state = RFC_STATE_CLOSED;
- RFCOMM_TRACE_WARNING(
- "%s: RFCOMM connection closed, index=%d, state=%d reason=%s[%d], "
- "UUID=%04X, bd_addr=%s, is_server=%d",
- __func__, p_port->inx, p_port->state, PORT_GetResultString(res), res,
- p_port->uuid, p_port->bd_addr.ToString().c_str(), p_port->is_server);
+ LOG(INFO) << __func__ << ": RFCOMM connection closed, index="
+ << std::to_string(p_port->handle)
+ << ", state=" << std::to_string(p_port->state)
+ << ", reason=" << PORT_GetResultString(res) << "["
+ << std::to_string(res) << "], UUID=" << loghex(p_port->uuid)
+ << ", bd_addr=" << p_port->bd_addr
+ << ", is_server=" << p_port->is_server;
port_release_port(p_port);
}
diff --git a/stack/rfcomm/port_utils.cc b/stack/rfcomm/port_utils.cc
index 9dc05c8..7798375 100644
--- a/stack/rfcomm/port_utils.cc
+++ b/stack/rfcomm/port_utils.cc
@@ -61,36 +61,36 @@
*
******************************************************************************/
tPORT* port_allocate_port(uint8_t dlci, const RawAddress& bd_addr) {
- tPORT* p_port = &rfc_cb.port.port[0];
- uint8_t xx, yy;
-
- for (xx = 0, yy = rfc_cb.rfc.last_port + 1; xx < MAX_RFC_PORTS; xx++, yy++) {
- if (yy >= MAX_RFC_PORTS) yy = 0;
-
- p_port = &rfc_cb.port.port[yy];
+ uint8_t port_index = rfc_cb.rfc.last_port_index + static_cast<uint8_t>(1);
+ // Loop at most MAX_RFC_PORTS items
+ for (int loop_counter = 0; loop_counter < MAX_RFC_PORTS;
+ loop_counter++, port_index++) {
+ if (port_index >= MAX_RFC_PORTS) {
+ port_index = 0;
+ }
+ tPORT* p_port = &rfc_cb.port.port[port_index];
if (!p_port->in_use) {
+ // Assume that we already called port_release_port on this
memset(p_port, 0, sizeof(tPORT));
-
p_port->in_use = true;
- p_port->inx = yy + 1;
-
- /* During the open set default state for the port connection */
+ // handle is a port handle starting from 1
+ p_port->handle = port_index + static_cast<uint8_t>(1);
+ // During the open set default state for the port connection
port_set_defaults(p_port);
-
p_port->rfc.port_timer = alarm_new("rfcomm_port.port_timer");
- rfc_cb.rfc.last_port = yy;
-
p_port->dlci = dlci;
p_port->bd_addr = bd_addr;
-
- RFCOMM_TRACE_DEBUG("rfc_cb.port.port[%d]:%p allocated, last_port:%d", yy,
- p_port, rfc_cb.rfc.last_port);
- VLOG(1) << __func__ << ": bd_addr:" << bd_addr;
- return (p_port);
+ rfc_cb.rfc.last_port_index = port_index;
+ RFCOMM_TRACE_DEBUG(
+ "%s: rfc_cb.port.port[%d]:%p chosen, "
+ "last_port_index:%d, bd_addr=%s",
+ __func__, port_index, p_port, rfc_cb.rfc.last_port_index,
+ bd_addr.ToString().c_str());
+ return p_port;
}
}
-
- /* If here, no free PORT found */
+ LOG(WARNING) << __func__ << ": running out of free ports for dlci "
+ << std::to_string(dlci) << ", bd_addr " << bd_addr;
return nullptr;
}
@@ -146,7 +146,7 @@
packet_size = btm_get_max_packet_size(p_port->bd_addr);
if (packet_size == 0) {
/* something is very wrong */
- RFCOMM_TRACE_WARNING("port_select_mtu bad packet size");
+ LOG(WARNING) << __func__ << ": bad packet size 0 for" << p_port->bd_addr;
p_port->mtu = RFCOMM_DEFAULT_MTU;
} else {
/* We try to negotiate MTU that each packet can be split into whole
@@ -166,17 +166,16 @@
p_port->mtu = ((L2CAP_MTU_SIZE + L2CAP_PKT_OVERHEAD) / packet_size *
packet_size) -
RFCOMM_DATA_OVERHEAD - L2CAP_PKT_OVERHEAD;
- RFCOMM_TRACE_DEBUG(
- "port_select_mtu selected %d based on connection speed",
- p_port->mtu);
+ RFCOMM_TRACE_DEBUG("%s: selected %d based on connection speed",
+ __func__, p_port->mtu);
} else {
p_port->mtu = L2CAP_MTU_SIZE - RFCOMM_DATA_OVERHEAD;
- RFCOMM_TRACE_DEBUG(
- "port_select_mtu selected %d based on l2cap PDU size", p_port->mtu);
+ RFCOMM_TRACE_DEBUG("%s: selected %d based on l2cap PDU size", __func__,
+ p_port->mtu);
}
}
} else {
- RFCOMM_TRACE_DEBUG("port_select_mtu application selected %d", p_port->mtu);
+ RFCOMM_TRACE_DEBUG("%s: application selected %d", __func__, p_port->mtu);
}
p_port->credit_rx_max = (PORT_RX_HIGH_WM / p_port->mtu);
if (p_port->credit_rx_max > PORT_RX_BUF_HIGH_WM)
@@ -188,7 +187,7 @@
if (p_port->rx_buf_critical > PORT_RX_BUF_CRITICAL_WM)
p_port->rx_buf_critical = PORT_RX_BUF_CRITICAL_WM;
RFCOMM_TRACE_DEBUG(
- "port_select_mtu credit_rx_max %d, credit_rx_low %d, rx_buf_critical %d",
+ "%s: credit_rx_max %d, credit_rx_low %d, rx_buf_critical %d", __func__,
p_port->credit_rx_max, p_port->credit_rx_low, p_port->rx_buf_critical);
}
@@ -226,7 +225,7 @@
if (p_port->rfc.state == RFC_STATE_CLOSED) {
if (p_port->rfc.p_mcb) {
- p_port->rfc.p_mcb->port_inx[p_port->dlci] = 0;
+ p_port->rfc.p_mcb->port_handles[p_port->dlci] = 0;
/* If there are no more ports opened on this MCB release it */
rfc_check_mcb_active(p_port->rfc.p_mcb);
@@ -242,7 +241,8 @@
mutex_global_unlock();
if (p_port->keep_port_handle) {
- RFCOMM_TRACE_DEBUG("%s Re-initialize handle: %d", __func__, p_port->inx);
+ RFCOMM_TRACE_DEBUG("%s Re-initialize handle: %d", __func__,
+ p_port->handle);
/* save event mask and callback */
uint32_t mask = p_port->ev_mask;
@@ -264,7 +264,7 @@
p_port->local_ctrl.modem_signal = p_port->default_signal_state;
p_port->bd_addr = RawAddress::kAny;
} else {
- RFCOMM_TRACE_DEBUG("%s Clean-up handle: %d", __func__, p_port->inx);
+ RFCOMM_TRACE_DEBUG("%s Clean-up handle: %d", __func__, p_port->handle);
alarm_free(p_port->rfc.port_timer);
memset(p_port, 0, sizeof(tPORT));
}
@@ -317,15 +317,14 @@
return nullptr;
}
- uint8_t inx = p_mcb->port_inx[dlci];
- if (inx == 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;
+ uint8_t handle = p_mcb->port_handles[dlci];
+ if (handle == 0) {
+ 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[inx - 1];
+ return &rfc_cb.port.port[handle - 1];
}
/*******************************************************************************
diff --git a/stack/rfcomm/rfc_int.h b/stack/rfcomm/rfc_int.h
index 05afa9d..22aab66 100644
--- a/stack/rfcomm/rfc_int.h
+++ b/stack/rfcomm/rfc_int.h
@@ -56,18 +56,23 @@
extern void RFCOMM_DlcReleaseReq(tRFC_MCB* p_mcb, uint8_t dlci);
-extern void RFCOMM_ParNegReq(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu);
-extern void RFCOMM_ParNegRsp(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu,
- uint8_t cl, uint8_t k);
+extern void RFCOMM_ParameterNegotiationRequest(tRFC_MCB* p_mcb, uint8_t dlci,
+ uint16_t mtu);
+extern void RFCOMM_ParameterNegotiationResponse(tRFC_MCB* p_mcb, uint8_t dlci,
+ uint16_t mtu, uint8_t cl,
+ uint8_t k);
extern void RFCOMM_TestReq(uint8_t* p_data, uint16_t len);
extern void RFCOMM_FlowReq(tRFC_MCB* p_mcb, uint8_t dlci, bool state);
-extern void RFCOMM_PortNegReq(tRFC_MCB* p_mcb, uint8_t dlci,
- tPORT_STATE* p_pars);
-extern void RFCOMM_PortNegRsp(tRFC_MCB* p_mcb, uint8_t dlci,
- tPORT_STATE* p_pars, uint16_t param_mask);
+extern void RFCOMM_PortParameterNegotiationRequest(tRFC_MCB* p_mcb,
+ uint8_t dlci,
+ tPORT_STATE* p_pars);
+extern void RFCOMM_PortParameterNegotiationResponse(tRFC_MCB* p_mcb,
+ uint8_t dlci,
+ tPORT_STATE* p_pars,
+ uint16_t param_mask);
extern void RFCOMM_ControlReq(tRFC_MCB* p_mcb, uint8_t dlci,
tPORT_CTRL* p_pars);
@@ -221,7 +226,7 @@
tRFC_MCB* p_rfc_lcid_mcb[MAX_L2CAP_CHANNELS];
bool peer_rx_disabled; /* If true peer sent FCOFF */
uint8_t last_mux; /* Last mux allocated */
- uint8_t last_port; /* Last port allocated */
+ uint8_t last_port_index; // Index of last port allocated in rfc_cb.port
} tRFCOMM_CB;
/* Main Control Block for the RFCOMM Layer (PORT and RFC) */
diff --git a/stack/rfcomm/rfc_l2cap_if.cc b/stack/rfcomm/rfc_l2cap_if.cc
index a20f399..380f5bf 100644
--- a/stack/rfcomm/rfc_l2cap_if.cc
+++ b/stack/rfcomm/rfc_l2cap_if.cc
@@ -110,14 +110,14 @@
} else {
/* we cannot accept connection request from peer at this state */
/* don't update lcid */
- p_mcb = NULL;
+ p_mcb = nullptr;
}
} else {
/* store mcb even if null */
rfc_save_lcid_mcb(p_mcb, lcid);
}
- if (p_mcb == NULL) {
+ if (p_mcb == nullptr) {
L2CA_ConnectRsp(bd_addr, id, lcid, L2CAP_CONN_NO_RESOURCES, 0);
return;
}
@@ -163,13 +163,13 @@
/* update direction bit */
for (int i = 0; i < RFCOMM_MAX_DLCI; i += 2) {
- uint8_t idx = p_mcb->port_inx[i];
- if (idx != 0) {
- p_mcb->port_inx[i] = 0;
- p_mcb->port_inx[i + 1] = idx;
- rfc_cb.port.port[idx - 1].dlci += 1;
- RFCOMM_TRACE_DEBUG("RFCOMM MX, port_handle=%d, DLCI[%d->%d]", idx, i,
- rfc_cb.port.port[idx - 1].dlci);
+ uint8_t handle = p_mcb->port_handles[i];
+ if (handle != 0) {
+ p_mcb->port_handles[i] = 0;
+ p_mcb->port_handles[i + 1] = handle;
+ rfc_cb.port.port[handle - 1].dlci += 1;
+ RFCOMM_TRACE_DEBUG("RFCOMM MX, port_handle=%d, DLCI[%d->%d]", handle,
+ i, rfc_cb.port.port[handle - 1].dlci);
}
}
@@ -254,17 +254,16 @@
*
******************************************************************************/
void RFCOMM_DisconnectInd(uint16_t lcid, bool is_conf_needed) {
+ VLOG(1) << __func__ << ": lcid=" << loghex(lcid)
+ << ", is_conf_needed=" << is_conf_needed;
tRFC_MCB* p_mcb = rfc_find_lcid_mcb(lcid);
-
if (is_conf_needed) {
L2CA_DisconnectRsp(lcid);
}
-
if (!p_mcb) {
- RFCOMM_TRACE_WARNING("RFCOMM_DisconnectInd LCID:0x%x", lcid);
+ LOG(WARNING) << __func__ << ": no mcb for lcid " << loghex(lcid);
return;
}
-
rfc_mx_sm_execute(p_mcb, RFC_MX_EVENT_DISC_IND, nullptr);
}
@@ -299,7 +298,7 @@
}
if (rfc_cb.rfc.rx_frame.dlci == RFCOMM_MX_DLCI) {
- RFCOMM_TRACE_DEBUG("%s: Handle multiplexer event %d, p_mcb=%p", __func__,
+ RFCOMM_TRACE_DEBUG("%s: handle multiplexer event %d, p_mcb=%p", __func__,
event, p_mcb);
/* Take special care of the Multiplexer Control Messages */
if (event == RFC_EVENT_UIH) {
@@ -341,10 +340,11 @@
osi_free(p_buf);
return;
}
- RFCOMM_TRACE_DEBUG("%s: port_inx[dlci=%d]:%d->%d, p_mcb=%p", __func__,
+ RFCOMM_TRACE_DEBUG("%s: port_handles[dlci=%d]:%d->%d, p_mcb=%p", __func__,
rfc_cb.rfc.rx_frame.dlci,
- p_mcb->port_inx[rfc_cb.rfc.rx_frame.dlci], p_port->inx);
- p_mcb->port_inx[rfc_cb.rfc.rx_frame.dlci] = p_port->inx;
+ p_mcb->port_handles[rfc_cb.rfc.rx_frame.dlci],
+ p_port->handle);
+ p_mcb->port_handles[rfc_cb.rfc.rx_frame.dlci] = p_port->handle;
p_port->rfc.p_mcb = p_mcb;
}
@@ -419,6 +419,14 @@
*
******************************************************************************/
void rfc_save_lcid_mcb(tRFC_MCB* p_mcb, uint16_t lcid) {
- if (lcid < L2CAP_BASE_APPL_CID) return;
- rfc_cb.rfc.p_rfc_lcid_mcb[lcid - L2CAP_BASE_APPL_CID] = p_mcb;
+ if (lcid < L2CAP_BASE_APPL_CID) {
+ LOG(ERROR) << __func__ << ": LCID " << lcid << " is too small";
+ return;
+ }
+ auto mcb_index = static_cast<size_t>(lcid - L2CAP_BASE_APPL_CID);
+ if (mcb_index >= MAX_L2CAP_CHANNELS) {
+ LOG(ERROR) << __func__ << ": LCID " << lcid << " is too large";
+ return;
+ }
+ rfc_cb.rfc.p_rfc_lcid_mcb[mcb_index] = p_mcb;
}
diff --git a/stack/rfcomm/rfc_mx_fsm.cc b/stack/rfcomm/rfc_mx_fsm.cc
index dfab62f..12ed1f7 100644
--- a/stack/rfcomm/rfc_mx_fsm.cc
+++ b/stack/rfcomm/rfc_mx_fsm.cc
@@ -69,6 +69,7 @@
*
******************************************************************************/
void rfc_mx_sm_execute(tRFC_MCB* p_mcb, uint16_t event, void* p_data) {
+ RFCOMM_TRACE_DEBUG("%s: STATE=%d, EVENT=%d", __func__, p_mcb->state, event);
switch (p_mcb->state) {
case RFC_MX_STATE_IDLE:
rfc_mx_sm_state_idle(p_mcb, event, p_data);
@@ -112,7 +113,7 @@
*
******************************************************************************/
void rfc_mx_sm_state_idle(tRFC_MCB* p_mcb, uint16_t event, void* p_data) {
- RFCOMM_TRACE_EVENT("rfc_mx_sm_state_idle - evt:%d", event);
+ RFCOMM_TRACE_EVENT("%s: evt %d", __func__, event);
switch (event) {
case RFC_MX_EVENT_START_REQ: {
/* Initialize L2CAP MTU */
@@ -120,7 +121,9 @@
uint16_t lcid = L2CA_ConnectReq(BT_PSM_RFCOMM, p_mcb->bd_addr);
if (lcid == 0) {
- rfc_save_lcid_mcb(NULL, p_mcb->lcid);
+ LOG(ERROR) << __func__ << ": failed to open L2CAP channel for "
+ << p_mcb->bd_addr;
+ rfc_save_lcid_mcb(nullptr, p_mcb->lcid);
p_mcb->lcid = 0;
PORT_StartCnf(p_mcb, RFCOMM_ERROR);
return;
@@ -182,7 +185,7 @@
******************************************************************************/
void rfc_mx_sm_state_wait_conn_cnf(tRFC_MCB* p_mcb, uint16_t event,
void* p_data) {
- RFCOMM_TRACE_EVENT("rfc_mx_sm_state_wait_conn_cnf - evt:%d", event);
+ RFCOMM_TRACE_EVENT("%s: evt %d", __func__, event);
switch (event) {
case RFC_MX_EVENT_START_REQ:
RFCOMM_TRACE_ERROR("Mx error state %d event %d", p_mcb->state, event);
@@ -218,7 +221,7 @@
/* we gave up outgoing connection request then try peer's request */
if (p_mcb->pending_lcid) {
uint16_t i;
- uint8_t idx;
+ uint8_t handle;
RFCOMM_TRACE_DEBUG(
"RFCOMM MX retry as acceptor in collision case - evt:%d in "
@@ -233,13 +236,13 @@
/* update direction bit */
for (i = 0; i < RFCOMM_MAX_DLCI; i += 2) {
- idx = p_mcb->port_inx[i];
- if (idx != 0) {
- p_mcb->port_inx[i] = 0;
- p_mcb->port_inx[i + 1] = idx;
- rfc_cb.port.port[idx - 1].dlci += 1;
+ handle = p_mcb->port_handles[i];
+ if (handle != 0) {
+ p_mcb->port_handles[i] = 0;
+ p_mcb->port_handles[i + 1] = handle;
+ rfc_cb.port.port[handle - 1].dlci += 1;
RFCOMM_TRACE_DEBUG("RFCOMM MX - DLCI:%d -> %d", i,
- rfc_cb.port.port[idx - 1].dlci);
+ rfc_cb.port.port[handle - 1].dlci);
}
}
@@ -264,7 +267,7 @@
*
******************************************************************************/
void rfc_mx_sm_state_configure(tRFC_MCB* p_mcb, uint16_t event, void* p_data) {
- RFCOMM_TRACE_EVENT("rfc_mx_sm_state_configure - evt:%d", event);
+ RFCOMM_TRACE_EVENT("%s: event %d", __func__, event);
switch (event) {
case RFC_MX_EVENT_START_REQ:
case RFC_MX_EVENT_CONN_CNF:
@@ -286,6 +289,8 @@
return;
case RFC_EVENT_TIMEOUT:
+ LOG(ERROR) << __func__ << ": L2CAP configuration timeout for "
+ << p_mcb->bd_addr;
p_mcb->state = RFC_MX_STATE_IDLE;
L2CA_DisconnectReq(p_mcb->lcid);
@@ -308,7 +313,7 @@
******************************************************************************/
void rfc_mx_sm_sabme_wait_ua(tRFC_MCB* p_mcb, uint16_t event,
UNUSED_ATTR void* p_data) {
- RFCOMM_TRACE_EVENT("rfc_mx_sm_sabme_wait_ua - evt:%d", event);
+ RFCOMM_TRACE_EVENT("%s: event %d", __func__, event);
switch (event) {
case RFC_MX_EVENT_START_REQ:
case RFC_MX_EVENT_CONN_CNF:
@@ -368,7 +373,7 @@
*
******************************************************************************/
void rfc_mx_sm_state_wait_sabme(tRFC_MCB* p_mcb, uint16_t event, void* p_data) {
- RFCOMM_TRACE_EVENT("rfc_mx_sm_state_wait_sabme - evt:%d", event);
+ RFCOMM_TRACE_EVENT("%s: event %d", __func__, event);
switch (event) {
case RFC_MX_EVENT_DISC_IND:
p_mcb->state = RFC_MX_STATE_IDLE;
@@ -431,7 +436,7 @@
******************************************************************************/
void rfc_mx_sm_state_connected(tRFC_MCB* p_mcb, uint16_t event,
UNUSED_ATTR void* p_data) {
- RFCOMM_TRACE_EVENT("rfc_mx_sm_state_connected - evt:%d", event);
+ RFCOMM_TRACE_EVENT("%s: event %d", __func__, event);
switch (event) {
case RFC_EVENT_TIMEOUT:
@@ -475,7 +480,7 @@
void* p_data) {
BT_HDR* p_buf;
- RFCOMM_TRACE_EVENT("rfc_mx_sm_state_disc_wait_ua - evt:%d", event);
+ RFCOMM_TRACE_EVENT("%s: event %d", __func__, event);
switch (event) {
case RFC_EVENT_UA:
case RFC_EVENT_DM:
@@ -549,12 +554,8 @@
*
******************************************************************************/
static void rfc_mx_send_config_req(tRFC_MCB* p_mcb) {
- tL2CAP_CFG_INFO cfg;
-
RFCOMM_TRACE_EVENT("rfc_mx_send_config_req");
-
- memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
-
+ tL2CAP_CFG_INFO cfg = {};
cfg.mtu_present = true;
cfg.mtu = L2CAP_MTU_SIZE;
@@ -580,11 +581,15 @@
*
******************************************************************************/
static void rfc_mx_conf_cnf(tRFC_MCB* p_mcb, tL2CAP_CFG_INFO* p_cfg) {
- RFCOMM_TRACE_EVENT("rfc_mx_conf_cnf p_cfg:%08x res:%d ", p_cfg,
+ RFCOMM_TRACE_EVENT("rfc_mx_conf_cnf p_cfg:%08x result:%d ", p_cfg,
(p_cfg) ? p_cfg->result : 0);
if (p_cfg->result != L2CAP_CFG_OK) {
+ LOG(ERROR) << __func__ << ": failed to configure L2CAP for "
+ << p_mcb->bd_addr;
if (p_mcb->is_initiator) {
+ LOG(ERROR) << __func__ << ": disconnect L2CAP due to config failure for "
+ << p_mcb->bd_addr;
PORT_StartCnf(p_mcb, p_cfg->result);
L2CA_DisconnectReq(p_mcb->lcid);
}
@@ -619,10 +624,11 @@
static void rfc_mx_conf_ind(tRFC_MCB* p_mcb, tL2CAP_CFG_INFO* p_cfg) {
/* Save peer L2CAP MTU if present */
/* RFCOMM adds 3-4 bytes in the beginning and 1 bytes FCS */
- if (p_cfg->mtu_present)
+ if (p_cfg->mtu_present) {
p_mcb->peer_l2cap_mtu = p_cfg->mtu - RFCOMM_MIN_OFFSET - 1;
- else
+ } else {
p_mcb->peer_l2cap_mtu = L2CAP_DEFAULT_MTU - RFCOMM_MIN_OFFSET - 1;
+ }
p_cfg->mtu_present = false;
p_cfg->flush_to_present = false;
diff --git a/stack/rfcomm/rfc_port_fsm.cc b/stack/rfcomm/rfc_port_fsm.cc
index 4ff4628..947c23b 100644
--- a/stack/rfcomm/rfc_port_fsm.cc
+++ b/stack/rfcomm/rfc_port_fsm.cc
@@ -64,8 +64,11 @@
*
******************************************************************************/
void rfc_port_sm_execute(tPORT* p_port, uint16_t event, void* p_data) {
+ VLOG(1) << __func__ << ": PORT=" << std::to_string(p_port->handle)
+ << ", STATE=" << std::to_string(p_port->rfc.state)
+ << ", EVENT=" << event;
if (!p_port) {
- RFCOMM_TRACE_WARNING("NULL port event %d", event);
+ LOG(WARNING) << __func__ << ": NULL port event " << event;
return;
}
@@ -143,7 +146,8 @@
return;
case RFC_EVENT_DM:
- RFCOMM_TRACE_WARNING("%s, RFC_EVENT_DM, index=%d", __func__, p_port->inx);
+ RFCOMM_TRACE_WARNING("%s, RFC_EVENT_DM, index=%d", __func__,
+ p_port->handle);
rfc_port_closed(p_port);
return;
@@ -194,7 +198,7 @@
case RFC_EVENT_CLEAR:
RFCOMM_TRACE_WARNING("%s, RFC_EVENT_CLEAR, index=%d", __func__,
- p_port->inx);
+ p_port->handle);
rfc_port_closed(p_port);
return;
@@ -210,7 +214,8 @@
return;
case RFC_EVENT_DM:
- RFCOMM_TRACE_WARNING("%s, RFC_EVENT_DM, index=%d", __func__, p_port->inx);
+ RFCOMM_TRACE_WARNING("%s, RFC_EVENT_DM, index=%d", __func__,
+ p_port->handle);
p_port->rfc.p_mcb->is_disc_initiator = true;
PORT_DlcEstablishCnf(p_port->rfc.p_mcb, p_port->dlci,
p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR);
@@ -219,7 +224,7 @@
case RFC_EVENT_DISC:
RFCOMM_TRACE_WARNING("%s, RFC_EVENT_DISC, index=%d", __func__,
- p_port->inx);
+ p_port->handle);
rfc_send_ua(p_port->rfc.p_mcb, p_port->dlci);
PORT_DlcEstablishCnf(p_port->rfc.p_mcb, p_port->dlci,
p_port->rfc.p_mcb->peer_l2cap_mtu, RFCOMM_ERROR);
@@ -282,7 +287,7 @@
case RFC_EVENT_CLEAR:
RFCOMM_TRACE_WARNING("%s, RFC_EVENT_CLEAR, index=%d", __func__,
- p_port->inx);
+ p_port->handle);
btm_sec_abort_access_req(p_port->rfc.p_mcb->bd_addr);
rfc_port_closed(p_port);
return;
@@ -339,7 +344,8 @@
case RFC_EVENT_SEC_COMPLETE:
if (*((uint8_t*)p_data) != BTM_SUCCESS) {
RFCOMM_TRACE_ERROR("%s, RFC_EVENT_SEC_COMPLETE, index=%d, result=%d",
- __func__, event, p_port->inx, *((uint8_t*)p_data));
+ __func__, event, p_port->handle,
+ *((uint8_t*)p_data));
p_port->rfc.p_mcb->is_disc_initiator = true;
PORT_DlcEstablishCnf(p_port->rfc.p_mcb, p_port->dlci, 0,
RFCOMM_SECURITY_ERR);
@@ -359,7 +365,7 @@
case RFC_EVENT_CLOSE:
RFCOMM_TRACE_WARNING("%s, RFC_EVENT_CLOSE, index=%d", __func__,
- p_port->inx);
+ p_port->handle);
btm_sec_abort_access_req(p_port->rfc.p_mcb->bd_addr);
rfc_port_closed(p_port);
return;
@@ -403,7 +409,7 @@
case RFC_EVENT_CLEAR:
RFCOMM_TRACE_WARNING("%s, RFC_EVENT_CLEAR, index=%d", __func__,
- p_port->inx);
+ p_port->handle);
rfc_port_closed(p_port);
return;
@@ -435,7 +441,8 @@
return;
case RFC_EVENT_DM:
- RFCOMM_TRACE_WARNING("%s, RFC_EVENT_DM, index=%d", __func__, p_port->inx);
+ RFCOMM_TRACE_WARNING("%s, RFC_EVENT_DM, index=%d", __func__,
+ p_port->handle);
PORT_DlcReleaseInd(p_port->rfc.p_mcb, p_port->dlci);
rfc_port_closed(p_port);
return;
@@ -484,7 +491,7 @@
case RFC_EVENT_CLEAR:
RFCOMM_TRACE_WARNING("%s, RFC_EVENT_CLEAR, index=%d", __func__, event,
- p_port->inx);
+ p_port->handle);
rfc_port_closed(p_port);
return;
@@ -498,7 +505,7 @@
case RFC_EVENT_DM:
RFCOMM_TRACE_WARNING("%s, RFC_EVENT_DM|RFC_EVENT_UA[%d], index=%d",
- __func__, event, p_port->inx);
+ __func__, event, p_port->handle);
rfc_port_closed(p_port);
return;
@@ -517,7 +524,7 @@
case RFC_EVENT_TIMEOUT:
RFCOMM_TRACE_ERROR("%s, RFC_EVENT_TIMEOUT, index=%d", __func__,
- p_port->inx);
+ p_port->handle);
rfc_port_closed(p_port);
return;
}
@@ -545,7 +552,9 @@
*
******************************************************************************/
void rfc_process_pn(tRFC_MCB* p_mcb, bool is_command, MX_FRAME* p_frame) {
- tPORT* p_port;
+ RFCOMM_TRACE_DEBUG("%s: is_initiator=%d, is_cmd=%d, state=%d, bd_addr=%s",
+ __func__, p_mcb->is_initiator, is_command, p_mcb->state,
+ p_mcb->bd_addr.ToString().c_str());
uint8_t dlci = p_frame->dlci;
if (is_command) {
@@ -563,7 +572,7 @@
return;
}
/* If we are not awaiting response just ignore it */
- p_port = port_find_mcb_dlci_port(p_mcb, dlci);
+ tPORT* p_port = port_find_mcb_dlci_port(p_mcb, dlci);
if ((p_port == nullptr) || !(p_port->rfc.expected_rsp & RFC_RSP_PN)) {
LOG(WARNING) << ": Ignore unwanted response, p_mcb=" << p_mcb
<< ", bd_addr=" << p_mcb->bd_addr
diff --git a/stack/rfcomm/rfc_port_if.cc b/stack/rfcomm/rfc_port_if.cc
index 2ce5f9e..097d774 100644
--- a/stack/rfcomm/rfc_port_if.cc
+++ b/stack/rfcomm/rfc_port_if.cc
@@ -115,7 +115,7 @@
/*******************************************************************************
*
- * Function RFCOMM_ParNegReq
+ * Function RFCOMM_ParameterNegotiationRequest
*
* Description This function is called by the user app to start
* DLC parameter negotiation. Port emulation can send this
@@ -124,7 +124,8 @@
* block.
*
******************************************************************************/
-void RFCOMM_ParNegReq(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu) {
+void RFCOMM_ParameterNegotiationRequest(tRFC_MCB* p_mcb, uint8_t dlci,
+ uint16_t mtu) {
uint8_t flow;
uint8_t cl;
uint8_t k;
@@ -166,14 +167,14 @@
/*******************************************************************************
*
- * Function RFCOMM_ParNegRsp
+ * Function RFCOMM_ParameterNegotiationResponse
*
* Description This function is called by the user app to acknowledge
* DLC parameter negotiation.
*
******************************************************************************/
-void RFCOMM_ParNegRsp(tRFC_MCB* p_mcb, uint8_t dlci, uint16_t mtu, uint8_t cl,
- uint8_t k) {
+void RFCOMM_ParameterNegotiationResponse(tRFC_MCB* p_mcb, uint8_t dlci,
+ uint16_t mtu, uint8_t cl, uint8_t k) {
if (p_mcb->state != RFC_MX_STATE_CONNECTED) return;
/* Send Parameter Negotiation Response UIH frame */
@@ -182,7 +183,7 @@
/*******************************************************************************
*
- * Function RFCOMM_PortNegReq
+ * Function RFCOMM_PortParameterNegotiationRequest
*
* Description This function is called by the user app to start
* Remote Port parameter negotiation. Port emulation can
@@ -191,7 +192,8 @@
* control block.
*
******************************************************************************/
-void RFCOMM_PortNegReq(tRFC_MCB* p_mcb, uint8_t dlci, tPORT_STATE* p_pars) {
+void RFCOMM_PortParameterNegotiationRequest(tRFC_MCB* p_mcb, uint8_t dlci,
+ tPORT_STATE* p_pars) {
if (p_mcb->state != RFC_MX_STATE_CONNECTED) {
PORT_PortNegCnf(p_mcb, dlci, nullptr, RFCOMM_ERROR);
return;
@@ -215,14 +217,15 @@
/*******************************************************************************
*
- * Function RFCOMM_PortNegRsp
+ * Function RFCOMM_PortParameterNegotiationResponse
*
* Description This function is called by the user app to acknowledge
* Port parameters negotiation.
*
******************************************************************************/
-void RFCOMM_PortNegRsp(tRFC_MCB* p_mcb, uint8_t dlci, tPORT_STATE* p_pars,
- uint16_t param_mask) {
+void RFCOMM_PortParameterNegotiationResponse(tRFC_MCB* p_mcb, uint8_t dlci,
+ tPORT_STATE* p_pars,
+ uint16_t param_mask) {
if (p_mcb->state != RFC_MX_STATE_CONNECTED) return;
rfc_send_rpn(p_mcb, dlci, false, p_pars, param_mask);
diff --git a/stack/rfcomm/rfc_ts_frames.cc b/stack/rfcomm/rfc_ts_frames.cc
index 0c8ce09..51fbcac 100644
--- a/stack/rfcomm/rfc_ts_frames.cc
+++ b/stack/rfcomm/rfc_ts_frames.cc
@@ -153,14 +153,19 @@
uint8_t credits;
p_buf->offset -= RFCOMM_CTRL_FRAME_LEN;
- if (p_buf->len > 127) p_buf->offset--;
+ if (p_buf->len > 127) {
+ p_buf->offset--;
+ }
- if (dlci)
+ if (dlci) {
credits = (uint8_t)p_buf->layer_specific;
- else
+ } else {
credits = 0;
+ }
- if (credits) p_buf->offset--;
+ if (credits) {
+ p_buf->offset--;
+ }
p_data = (uint8_t*)(p_buf + 1) + p_buf->offset;
@@ -218,11 +223,11 @@
** We will use the fact that we reply in the same context so rx_frame can
*still be used.
*/
- if (is_command)
+ if (is_command) {
*p_data++ = RFCOMM_PN_PRIORITY_0;
- else
+ } else {
*p_data++ = rfc_cb.rfc.rx_frame.u.pn.priority;
-
+ }
*p_data++ = RFCOMM_T1_DSEC;
*p_data++ = mtu & 0xFF;
*p_data++ = mtu >> 8;
@@ -527,8 +532,9 @@
p_frame->credit = *p_data++;
p_buf->len--;
p_buf->offset++;
- } else
+ } else {
p_frame->credit = 0;
+ }
if (p_buf->len != len) {
RFCOMM_TRACE_ERROR("Bad Length2 %d %d", p_buf->len, len);
@@ -589,8 +595,9 @@
/* we assume that this is ok to allow bad implementations to work */
RFCOMM_TRACE_ERROR("Bad UIH - response");
return (RFC_EVENT_UIH);
- } else
+ } else {
return (RFC_EVENT_UIH);
+ }
}
return (RFC_EVENT_BAD_FRAME);
@@ -609,7 +616,6 @@
MX_FRAME* p_rx_frame = &rfc_cb.rfc.rx_frame;
uint16_t length = p_buf->len;
uint8_t ea, cr, mx_len;
- bool is_command;
p_rx_frame->ea = *p_data & RFCOMM_EA;
p_rx_frame->cr = (*p_data & RFCOMM_CR_MASK) >> RFCOMM_SHIFT_CR;
@@ -625,7 +631,7 @@
length--;
- is_command = p_rx_frame->cr;
+ bool is_command = p_rx_frame->cr;
ea = *p_data & RFCOMM_EA;
@@ -644,8 +650,8 @@
return;
}
- RFCOMM_TRACE_DEBUG("%s: type=%d, p_mcb=%p", __func__, p_rx_frame->type,
- p_mcb);
+ RFCOMM_TRACE_DEBUG("%s: type=0x%02x, bd_addr=%s", __func__, p_rx_frame->type,
+ p_mcb->bd_addr.ToString().c_str());
switch (p_rx_frame->type) {
case RFCOMM_MX_PN:
if (length != RFCOMM_MX_PN_LEN) {
diff --git a/stack/rfcomm/rfc_utils.cc b/stack/rfcomm/rfc_utils.cc
index 74e336a..616e24e 100644
--- a/stack/rfcomm/rfc_utils.cc
+++ b/stack/rfcomm/rfc_utils.cc
@@ -285,7 +285,7 @@
uint16_t i;
for (i = 0; i < RFCOMM_MAX_DLCI; i++) {
- if (p_mcb->port_inx[i] != 0) {
+ if (p_mcb->port_handles[i] != 0) {
p_mcb->is_disc_initiator = false;
return;
}
@@ -353,7 +353,7 @@
/* If multiplexer channel was up mark it as down */
if (p_mcb) {
- p_mcb->port_inx[p_port->dlci] = 0;
+ p_mcb->port_handles[p_port->dlci] = 0;
/* If there are no more ports opened on this MCB release it */
rfc_check_mcb_active(p_mcb);
diff --git a/stack/test/common/mock_btm_layer.cc b/stack/test/common/mock_btm_layer.cc
new file mode 100644
index 0000000..1405c55
--- /dev/null
+++ b/stack/test/common/mock_btm_layer.cc
@@ -0,0 +1,45 @@
+/******************************************************************************
+ *
+ * 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 "mock_btm_layer.h"
+
+static bluetooth::manager::MockBtmSecurityInternalInterface*
+ btm_security_internal_interface = nullptr;
+
+void bluetooth::manager::SetMockSecurityInternalInterface(
+ MockBtmSecurityInternalInterface* mock_btm_security_internal_interface) {
+ btm_security_internal_interface = mock_btm_security_internal_interface;
+}
+
+void btm_sec_abort_access_req(const RawAddress& bd_addr) {
+ btm_security_internal_interface->AbortAccessRequest(bd_addr);
+}
+
+tBTM_STATUS btm_sec_mx_access_request(const RawAddress& bd_addr, uint16_t psm,
+ bool is_originator, uint32_t mx_proto_id,
+ uint32_t mx_chan_id,
+ tBTM_SEC_CALLBACK* p_callback,
+ void* p_ref_data) {
+ return btm_security_internal_interface->MultiplexingProtocolAccessRequest(
+ bd_addr, psm, is_originator, mx_proto_id, mx_chan_id, p_callback,
+ p_ref_data);
+}
+
+uint16_t btm_get_max_packet_size(const RawAddress& addr) {
+ return RFCOMM_DEFAULT_MTU;
+}
\ No newline at end of file
diff --git a/stack/test/common/mock_btm_layer.h b/stack/test/common/mock_btm_layer.h
new file mode 100644
index 0000000..45f32d1
--- /dev/null
+++ b/stack/test/common/mock_btm_layer.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "btm_int.h"
+
+namespace bluetooth {
+namespace manager {
+
+class BtmSecurityInternalInterface {
+ public:
+ virtual void AbortAccessRequest(const RawAddress& bd_addr) = 0;
+ virtual tBTM_STATUS MultiplexingProtocolAccessRequest(
+ const RawAddress& bd_addr, uint16_t psm, bool is_originator,
+ uint32_t mx_proto_id, uint32_t mx_chan_id, tBTM_SEC_CALLBACK* p_callback,
+ void* p_ref_data) = 0;
+ virtual ~BtmSecurityInternalInterface() = default;
+};
+
+class MockBtmSecurityInternalInterface : public BtmSecurityInternalInterface {
+ public:
+ MOCK_METHOD1(AbortAccessRequest, void(const RawAddress& bd_addr));
+ MOCK_METHOD7(MultiplexingProtocolAccessRequest,
+ tBTM_STATUS(const RawAddress& bd_addr, uint16_t psm,
+ bool is_originator, uint32_t mx_proto_id,
+ uint32_t mx_chan_id, tBTM_SEC_CALLBACK* p_callback,
+ void* p_ref_data));
+};
+
+/**
+ * Set the {@link MockBtmSecurityInternalInterface} for testing
+ *
+ * @param mock_btm_security_internal_interface pointer to mock btm security
+ * internal interface, could be null
+ */
+void SetMockSecurityInternalInterface(
+ MockBtmSecurityInternalInterface* mock_btm_security_internal_interface);
+
+} // namespace manager
+} // namespace bluetooth
\ No newline at end of file
diff --git a/stack/test/common/mock_btu_layer.cc b/stack/test/common/mock_btu_layer.cc
new file mode 100644
index 0000000..f5bbf7a
--- /dev/null
+++ b/stack/test/common/mock_btu_layer.cc
@@ -0,0 +1,21 @@
+/******************************************************************************
+ *
+ * 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/message_loop/message_loop.h>
+
+base::MessageLoop* get_message_loop() { return nullptr; }
\ No newline at end of file
diff --git a/stack/test/common/mock_l2cap_layer.cc b/stack/test/common/mock_l2cap_layer.cc
new file mode 100644
index 0000000..9f08d56
--- /dev/null
+++ b/stack/test/common/mock_l2cap_layer.cc
@@ -0,0 +1,59 @@
+/******************************************************************************
+ *
+ * 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 "mock_l2cap_layer.h"
+
+static bluetooth::l2cap::MockL2capInterface* l2cap_interface = nullptr;
+
+void bluetooth::l2cap::SetMockInterface(
+ MockL2capInterface* mock_l2cap_interface) {
+ l2cap_interface = mock_l2cap_interface;
+}
+
+uint16_t L2CA_Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info) {
+ VLOG(1) << __func__ << ": psm=" << psm << ", p_cb_info=" << p_cb_info;
+ return l2cap_interface->Register(psm, p_cb_info);
+}
+
+uint16_t L2CA_ConnectReq(uint16_t psm, const RawAddress& bd_addr) {
+ return l2cap_interface->ConnectRequest(psm, bd_addr);
+}
+
+bool L2CA_ConnectRsp(const RawAddress& bd_addr, uint8_t id, uint16_t lcid,
+ uint16_t result, uint16_t status) {
+ return l2cap_interface->ConnectResponse(bd_addr, id, lcid, result, status);
+}
+
+bool L2CA_DisconnectReq(uint16_t cid) {
+ return l2cap_interface->DisconnectRequest(cid);
+}
+
+bool L2CA_DisconnectRsp(uint16_t cid) {
+ return l2cap_interface->DisconnectResponse(cid);
+}
+
+bool L2CA_ConfigReq(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) {
+ return l2cap_interface->ConfigRequest(cid, p_cfg);
+}
+
+bool L2CA_ConfigRsp(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) {
+ return l2cap_interface->ConfigResponse(cid, p_cfg);
+}
+
+uint8_t L2CA_DataWrite(uint16_t cid, BT_HDR* p_data) {
+ return l2cap_interface->DataWrite(cid, p_data);
+}
diff --git a/stack/test/common/mock_l2cap_layer.h b/stack/test/common/mock_l2cap_layer.h
new file mode 100644
index 0000000..78c8a9c
--- /dev/null
+++ b/stack/test/common/mock_l2cap_layer.h
@@ -0,0 +1,65 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include <gmock/gmock.h>
+
+#include "l2c_api.h"
+
+namespace bluetooth {
+namespace l2cap {
+
+class L2capInterface {
+ public:
+ virtual uint16_t Register(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info) = 0;
+ virtual uint16_t ConnectRequest(uint16_t psm, const RawAddress& bd_addr) = 0;
+ virtual bool ConnectResponse(const RawAddress& bd_addr, uint8_t id,
+ uint16_t lcid, uint16_t result,
+ uint16_t status) = 0;
+ virtual bool DisconnectRequest(uint16_t cid) = 0;
+ virtual bool DisconnectResponse(uint16_t cid) = 0;
+ virtual bool ConfigRequest(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) = 0;
+ virtual bool ConfigResponse(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) = 0;
+ virtual uint8_t DataWrite(uint16_t cid, BT_HDR* p_data) = 0;
+ virtual ~L2capInterface() = default;
+};
+
+class MockL2capInterface : public L2capInterface {
+ public:
+ MOCK_METHOD2(Register, uint16_t(uint16_t psm, tL2CAP_APPL_INFO* p_cb_info));
+ MOCK_METHOD2(ConnectRequest,
+ uint16_t(uint16_t psm, const RawAddress& bd_addr));
+ MOCK_METHOD5(ConnectResponse,
+ bool(const RawAddress& bd_addr, uint8_t id, uint16_t lcid,
+ uint16_t result, uint16_t status));
+ MOCK_METHOD1(DisconnectRequest, bool(uint16_t cid));
+ MOCK_METHOD1(DisconnectResponse, bool(uint16_t cid));
+ MOCK_METHOD2(ConfigRequest, bool(uint16_t cid, tL2CAP_CFG_INFO* p_cfg));
+ MOCK_METHOD2(ConfigResponse, bool(uint16_t cid, tL2CAP_CFG_INFO* p_cfg));
+ MOCK_METHOD2(DataWrite, uint8_t(uint16_t cid, BT_HDR* p_data));
+};
+
+/**
+ * Set the {@link MockL2capInterface} for testing
+ *
+ * @param mock_l2cap_interface pointer to mock l2cap interface, could be null
+ */
+void SetMockInterface(MockL2capInterface* mock_l2cap_interface);
+
+} // namespace l2cap
+} // namespace bluetooth
diff --git a/stack/test/common/stack_test_packet_utils.cc b/stack/test/common/stack_test_packet_utils.cc
new file mode 100644
index 0000000..74fc57d
--- /dev/null
+++ b/stack/test/common/stack_test_packet_utils.cc
@@ -0,0 +1,96 @@
+/******************************************************************************
+ *
+ * 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 "hci_layer.h"
+#include "l2c_api.h"
+#include "osi/include/allocator.h"
+
+#include "stack_test_packet_utils.h"
+
+namespace bluetooth {
+
+std::vector<uint8_t> CreateL2capDataPacket(uint16_t lcid,
+ const std::vector<uint8_t>& data) {
+ // Data in little endian order
+ std::vector<uint8_t> result;
+ auto data_size = static_cast<uint16_t>(data.size());
+ result.push_back(static_cast<uint8_t>(data_size));
+ result.push_back(static_cast<uint8_t>(data_size >> 8));
+ result.push_back(static_cast<uint8_t>(lcid));
+ result.push_back(static_cast<uint8_t>(lcid >> 8));
+ result.insert(result.end(), data.begin(), data.end());
+ return result;
+}
+
+std::vector<uint8_t> CreateAclPacket(uint16_t handle, uint8_t pb, uint8_t bc,
+ const std::vector<uint8_t>& data) {
+ // Data in little endian order
+ std::vector<uint8_t> result;
+ result.push_back(static_cast<uint8_t>(handle & 0x0F));
+ uint8_t second_byte = 0;
+ second_byte |= (bc << 6) & 0b11000000;
+ second_byte |= (pb << 4) & 0b00110000;
+ second_byte |= (handle >> 8) & 0b00001111;
+ result.push_back(second_byte);
+ auto data_size = static_cast<uint16_t>(data.size());
+ result.push_back(static_cast<uint8_t>(data_size));
+ result.push_back(static_cast<uint8_t>(data_size >> 8));
+ result.insert(result.end(), data.begin(), data.end());
+ return result;
+}
+
+BT_HDR* AllocateWrappedIncomingL2capAclPacket(const uint8_t* acl_packet_bytes,
+ size_t buffer_length) {
+ size_t packet_size = buffer_length + BT_HDR_SIZE;
+ auto packet = reinterpret_cast<BT_HDR*>(osi_malloc(packet_size));
+ // Add ACL packet overhead + L2CAP packet overhead
+ packet->offset = 4 + L2CAP_PKT_OVERHEAD;
+ packet->len = static_cast<uint16_t>(buffer_length - 4 - L2CAP_PKT_OVERHEAD);
+ packet->layer_specific = 0;
+ packet->event = MSG_HC_TO_STACK_HCI_ACL;
+ memcpy(packet->data, acl_packet_bytes, buffer_length);
+ return packet;
+}
+
+BT_HDR* AllocateWrappedIncomingL2capAclPacket(
+ const std::vector<uint8_t>& buffer) {
+ return AllocateWrappedIncomingL2capAclPacket(buffer.data(), buffer.size());
+}
+
+BT_HDR* AllocateWrappedOutgoingL2capAclPacket(const uint8_t* acl_packet_bytes,
+ size_t buffer_length) {
+ size_t acl_l2cap_header_size = 4 + L2CAP_PKT_OVERHEAD;
+ CHECK_GE(L2CAP_MIN_OFFSET, static_cast<int>(acl_l2cap_header_size));
+ size_t packet_size =
+ BT_HDR_SIZE + L2CAP_MIN_OFFSET + buffer_length - acl_l2cap_header_size;
+ auto packet = reinterpret_cast<BT_HDR*>(osi_malloc(packet_size));
+ packet->offset = L2CAP_MIN_OFFSET;
+ packet->len = static_cast<uint16_t>(buffer_length - acl_l2cap_header_size);
+ packet->layer_specific = 0;
+ packet->event = 0;
+ memcpy(packet->data + packet->offset - acl_l2cap_header_size,
+ acl_packet_bytes, buffer_length);
+ return packet;
+}
+
+BT_HDR* AllocateWrappedOutgoingL2capAclPacket(
+ const std::vector<uint8_t>& buffer) {
+ return AllocateWrappedOutgoingL2capAclPacket(buffer.data(), buffer.size());
+}
+
+} // namespace bluetooth
\ No newline at end of file
diff --git a/stack/test/common/stack_test_packet_utils.h b/stack/test/common/stack_test_packet_utils.h
new file mode 100644
index 0000000..fcbc5a9
--- /dev/null
+++ b/stack/test/common/stack_test_packet_utils.h
@@ -0,0 +1,78 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include "bt_types.h"
+
+namespace bluetooth {
+
+/**
+ * Create L2CAP data packet
+ *
+ * @param lcid
+ * @param data
+ * @return vector of bytes
+ */
+std::vector<uint8_t> CreateL2capDataPacket(uint16_t lcid,
+ const std::vector<uint8_t>& data);
+
+/**
+ * Create ACL data packet
+ *
+ * @param handle ACL connection hanle
+ * @param pb pb byte
+ * @param bc bc byte
+ * @param data frame data
+ * @return vector of bytes
+ */
+std::vector<uint8_t> CreateAclPacket(uint16_t handle, uint8_t pb, uint8_t bc,
+ const std::vector<uint8_t>& data);
+
+/**
+ * Given an array of ACL packet bytes from BTSNOOP log, allocate an OSI
+ * allocated BT_HDR pointer to a packet that can be processed by L2CAP
+ * application layer
+ *
+ * Note: BT_HDR offset is configured for incoming packets
+ *
+ * @param acl_packet_bytes pointer to array of ACL packet bytes
+ * @param buffer_length length of the packet buffer
+ * @return BT_HDR pointer to an OSI heap allocated packet
+ */
+BT_HDR* AllocateWrappedIncomingL2capAclPacket(const uint8_t* acl_packet_bytes,
+ size_t buffer_length);
+BT_HDR* AllocateWrappedIncomingL2capAclPacket(
+ const std::vector<uint8_t>& buffer);
+
+/**
+ * Given an array of ACL packet bytes from BTSNOOP log, allocate an OSI
+ * allocated BT_HDR pointer to a packet that can be processed by L2CAP
+ * application layer
+ *
+ * Note: BT_HDR offset is configured for outgoing packets
+ *
+ * @param acl_packet_bytes pointer to array of ACL packet bytes
+ * @param buffer_length length of the packet buffer
+ * @return BT_HDR pointer to an OSI heap allocated packet
+ */
+BT_HDR* AllocateWrappedOutgoingL2capAclPacket(const uint8_t* acl_packet_bytes,
+ size_t buffer_length);
+BT_HDR* AllocateWrappedOutgoingL2capAclPacket(
+ const std::vector<uint8_t>& buffer);
+
+} // namespace bluetooth
diff --git a/stack/test/rfcomm/stack_rfcomm_test.cc b/stack/test/rfcomm/stack_rfcomm_test.cc
new file mode 100644
index 0000000..3d9da3f
--- /dev/null
+++ b/stack/test/rfcomm/stack_rfcomm_test.cc
@@ -0,0 +1,892 @@
+/******************************************************************************
+ *
+ * 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
diff --git a/stack/test/rfcomm/stack_rfcomm_test_main.cc b/stack/test/rfcomm/stack_rfcomm_test_main.cc
new file mode 100644
index 0000000..b6a7b25
--- /dev/null
+++ b/stack/test/rfcomm/stack_rfcomm_test_main.cc
@@ -0,0 +1,74 @@
+/******************************************************************************
+ *
+ * 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/command_line.h>
+#include <base/logging.h>
+#include <base/strings/string_piece.h>
+#include <base/strings/string_util.h>
+#include <gtest/gtest.h>
+
+#include <string>
+
+#include "bt_trace.h"
+
+// Override LogMsg method so that we can output log via VLOG(1)
+void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {
+ char buffer[256];
+ va_list args;
+ va_start(args, fmt_str);
+ vsnprintf(buffer, 256, fmt_str, args);
+ VLOG(1) << buffer;
+ va_end(args);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ if (base::CommandLine::InitializedForCurrentProcess()) {
+ LOG(FATAL) << "base::CommandLine::Init should not be called twice";
+ return 1;
+ }
+
+ const char* log_level_arg = nullptr;
+ for (int i = 0; i < argc; ++i) {
+ if (base::StartsWith(base::StringPiece(argv[i]), base::StringPiece("--v="),
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ log_level_arg = argv[i];
+ }
+ }
+ if (log_level_arg == nullptr) {
+ log_level_arg = "--v=0";
+ }
+ const char* logging_argv[] = {"bt_stack", log_level_arg};
+ // Init command line object with logging switches
+ if (!base::CommandLine::Init(2, logging_argv)) {
+ LOG(FATAL) << "base::CommandLine::Init failed, arg0=" << logging_argv[0]
+ << ", arg1=" << logging_argv[1];
+ return 1;
+ }
+
+ logging::LoggingSettings log_settings;
+ if (!logging::InitLogging(log_settings)) {
+ LOG(ERROR) << "Failed to set up logging";
+ }
+
+ // Android already logs thread_id, proc_id, timestamp, so disable those.
+ logging::SetLogItems(false, false, false, false);
+
+ return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/stack/test/rfcomm/stack_rfcomm_test_utils.cc b/stack/test/rfcomm/stack_rfcomm_test_utils.cc
new file mode 100644
index 0000000..f7745c9
--- /dev/null
+++ b/stack/test/rfcomm/stack_rfcomm_test_utils.cc
@@ -0,0 +1,223 @@
+/******************************************************************************
+ *
+ * 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 <bitset>
+#include <string>
+#include <vector>
+
+#include "rfc_int.h"
+#include "stack_test_packet_utils.h"
+
+#include "stack_rfcomm_test_utils.h"
+
+namespace bluetooth {
+namespace rfcomm {
+
+uint8_t GetDlci(bool on_originator_side, uint8_t scn) {
+ return static_cast<uint8_t>((scn << 1) + (on_originator_side ? 1 : 0));
+}
+
+uint8_t GetAddressField(bool ea, bool cr, uint8_t dlci) {
+ std::bitset<8> address;
+ address.set(0, ea);
+ // For UIH frame, cr for initiating device is 1, otherwise 0
+ // Otherwise:
+ // Command: Initiator -> Responder: 1
+ // Command: Responder -> Initiator 0
+ // Response: Initiator -> Responder 0
+ // Response: Responder -> Initiator 1
+ // Initiator is defined by the one who send SABM=1 command
+ address.set(1, cr);
+ address |= dlci << 2;
+ return static_cast<uint8_t>(address.to_ulong());
+}
+
+uint8_t GetControlField(bool pf, uint8_t frame_type) {
+ std::bitset<8> control;
+ control |= frame_type;
+ control.set(4, pf);
+ return static_cast<uint8_t>(control.to_ulong());
+}
+
+uint8_t GetFrameTypeFromControlField(uint8_t control_field) {
+ return static_cast<uint8_t>(control_field & ~(0b10000));
+}
+
+std::vector<uint8_t> CreateMccPnFrame(uint8_t dlci, uint8_t i_bits,
+ uint8_t cl_bits, uint8_t priority,
+ uint8_t timer_value, uint16_t rfcomm_mtu,
+ uint8_t max_num_retransmission,
+ uint8_t err_recovery_window_k) {
+ // Data in little endian order
+ std::vector<uint8_t> result;
+ result.push_back(static_cast<uint8_t>(dlci & 0b00111111));
+ result.push_back(static_cast<uint8_t>((cl_bits << 4) | (i_bits & 0x0F)));
+ result.push_back(static_cast<uint8_t>(priority & 0b00111111));
+ result.push_back(timer_value);
+ result.push_back(static_cast<uint8_t>(rfcomm_mtu));
+ result.push_back(static_cast<uint8_t>(rfcomm_mtu >> 8));
+ result.push_back(max_num_retransmission);
+ result.push_back(static_cast<uint8_t>(err_recovery_window_k & 0b111));
+ return result;
+}
+
+std::vector<uint8_t> CreateMccMscFrame(uint8_t dlci, bool fc, bool rtc,
+ bool rtr, bool ic, bool dv) {
+ // Data in little endian order
+ std::vector<uint8_t> result;
+ result.push_back(static_cast<uint8_t>((dlci << 2) | 0b11));
+ std::bitset<8> v24_signals;
+ // EA = 1, single byte
+ v24_signals.set(0, true);
+ v24_signals.set(1, fc);
+ v24_signals.set(2, rtc);
+ v24_signals.set(3, rtr);
+ v24_signals.set(6, ic);
+ v24_signals.set(7, dv);
+ result.push_back(static_cast<uint8_t>(v24_signals.to_ulong()));
+ return result;
+}
+
+std::vector<uint8_t> CreateMultiplexerControlFrame(
+ uint8_t command_type, bool cr, const std::vector<uint8_t>& data) {
+ // Data in little endian order
+ std::vector<uint8_t> result;
+ std::bitset<8> header;
+ header.set(0, true); // EA is always 1
+ header.set(1, cr);
+ header |= command_type << 2;
+ result.push_back(static_cast<uint8_t>(header.to_ulong()));
+ // 7 bit length + EA(1)
+ result.push_back(static_cast<uint8_t>((data.size() << 1) + 1));
+ result.insert(result.end(), data.begin(), data.end());
+ return result;
+}
+
+std::vector<uint8_t> CreateRfcommPacket(uint8_t address, uint8_t control,
+ int credits,
+ const std::vector<uint8_t>& data) {
+ // Data in little endian order
+ std::vector<uint8_t> result;
+ result.push_back(address);
+ result.push_back(control);
+ size_t length = data.size();
+ if ((length & 0b1000000) != 0) {
+ // 15 bits of length in little endian order + EA(0)
+ // Lower 7 bits + EA(0)
+ result.push_back(static_cast<uint8_t>(length) << 1);
+ // Upper 8 bits
+ result.push_back(static_cast<uint8_t>(length >> 8));
+ } else {
+ // 7 bits of length + EA(1)
+ result.push_back(static_cast<uint8_t>((length << 1) + 1));
+ }
+ if (credits > 0) {
+ result.push_back(static_cast<uint8_t>(credits));
+ }
+ result.insert(result.end(), data.begin(), data.end());
+ if (GetFrameTypeFromControlField(control) == RFCOMM_UIH) {
+ result.push_back(rfc_calc_fcs(2, result.data()));
+ } else {
+ result.push_back(
+ rfc_calc_fcs(static_cast<uint16_t>(result.size()), result.data()));
+ }
+ return result;
+}
+
+std::vector<uint8_t> CreateQuickUaPacket(uint8_t dlci, uint16_t l2cap_lcid,
+ uint16_t acl_handle) {
+ uint8_t address_field = GetAddressField(true, true, dlci);
+ uint8_t control_field = GetControlField(true, RFCOMM_UA);
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, -1, {});
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(l2cap_lcid, rfcomm_packet);
+ return CreateAclPacket(acl_handle, 0b10, 0b00, l2cap_packet);
+}
+
+std::vector<uint8_t> CreateQuickSabmPacket(uint8_t dlci, uint16_t l2cap_lcid,
+ uint16_t acl_handle) {
+ uint8_t address_field = GetAddressField(true, true, dlci);
+ uint8_t control_field = GetControlField(true, RFCOMM_SABME);
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, -1, {});
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(l2cap_lcid, rfcomm_packet);
+ return CreateAclPacket(acl_handle, 0b10, 0b00, l2cap_packet);
+}
+
+std::vector<uint8_t> CreateQuickPnPacket(bool rfc_cr, uint8_t target_dlci,
+ bool mx_cr, uint16_t rfc_mtu,
+ uint8_t cl, uint8_t priority,
+ uint8_t k, uint16_t l2cap_lcid,
+ uint16_t acl_handle) {
+ uint8_t address_field = GetAddressField(true, rfc_cr, RFCOMM_MX_DLCI);
+ uint8_t control_field = GetControlField(false, RFCOMM_UIH);
+ std::vector<uint8_t> mcc_pn_data = CreateMccPnFrame(
+ target_dlci, 0x0, cl, priority, RFCOMM_T1_DSEC, rfc_mtu, RFCOMM_N2, k);
+ std::vector<uint8_t> mcc_payload =
+ CreateMultiplexerControlFrame(0x20, mx_cr, mcc_pn_data);
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, -1, mcc_payload);
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(l2cap_lcid, rfcomm_packet);
+ return CreateAclPacket(acl_handle, 0b10, 0b00, l2cap_packet);
+}
+
+std::vector<uint8_t> CreateQuickMscPacket(bool rfc_cr, uint8_t dlci,
+ uint16_t l2cap_lcid,
+ uint16_t acl_handle, bool mx_cr,
+ bool fc, bool rtc, bool rtr, bool ic,
+ bool dv) {
+ uint8_t address_field = GetAddressField(true, rfc_cr, RFCOMM_MX_DLCI);
+ uint8_t control_field = GetControlField(false, RFCOMM_UIH);
+ std::vector<uint8_t> mcc_msc_data =
+ CreateMccMscFrame(dlci, fc, rtc, rtr, ic, dv);
+ std::vector<uint8_t> mcc_payload =
+ CreateMultiplexerControlFrame(0x38, mx_cr, mcc_msc_data);
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, -1, mcc_payload);
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(l2cap_lcid, rfcomm_packet);
+ return CreateAclPacket(acl_handle, 0b10, 0b00, l2cap_packet);
+}
+
+std::vector<uint8_t> CreateQuickDataPacket(uint8_t dlci, bool cr,
+ uint16_t l2cap_lcid,
+ uint16_t acl_handle, int credits,
+ const std::vector<uint8_t>& data) {
+ uint8_t address_field = GetAddressField(true, cr, dlci);
+ uint8_t control_field =
+ GetControlField(credits > 0 ? true : false, RFCOMM_UIH);
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, credits, data);
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(l2cap_lcid, rfcomm_packet);
+ return CreateAclPacket(acl_handle, 0b10, 0b00, l2cap_packet);
+}
+
+std::vector<uint8_t> CreateQuickDataPacket(uint8_t dlci, bool cr,
+ uint16_t l2cap_lcid,
+ uint16_t acl_handle, int credits,
+ const std::string& str) {
+ std::vector<uint8_t> data(str.begin(), str.end());
+ return CreateQuickDataPacket(dlci, cr, l2cap_lcid, acl_handle, credits, data);
+}
+
+} // namespace rfcomm
+} // namespace bluetooth
\ No newline at end of file
diff --git a/stack/test/rfcomm/stack_rfcomm_test_utils.h b/stack/test/rfcomm/stack_rfcomm_test_utils.h
new file mode 100644
index 0000000..c7fee43
--- /dev/null
+++ b/stack/test/rfcomm/stack_rfcomm_test_utils.h
@@ -0,0 +1,232 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ ******************************************************************************/
+#pragma once
+
+#include <vector>
+
+#include <gmock/gmock.h>
+
+#include "bt_types.h"
+
+namespace bluetooth {
+namespace rfcomm {
+
+class RfcommCallback {
+ public:
+ virtual void PortManagementCallback(uint32_t code, uint16_t port_handle,
+ uint16_t callback_index) = 0;
+ virtual void PortEventCallback(uint32_t code, uint16_t port_handle,
+ uint16_t callback_index) = 0;
+ virtual ~RfcommCallback() = default;
+};
+
+class MockRfcommCallback : public RfcommCallback {
+ public:
+ MOCK_METHOD3(PortManagementCallback, void(uint32_t code, uint16_t port_handle,
+ uint16_t callback_index));
+ MOCK_METHOD3(PortEventCallback, void(uint32_t code, uint16_t port_handle,
+ uint16_t callback_index));
+};
+
+/**
+ * Create DLCI using direction of service channel number
+ *
+ * @param on_originator_side is this a channel on initiator side
+ * @param scn service channel number
+ * @return DLCI
+ */
+uint8_t GetDlci(bool on_originator_side, uint8_t scn);
+
+/**
+ * Create address field in RFCOMM packet
+ *
+ * @param ea end of field byte, true if field only has 1 byte, false otherwise
+ * @param cr command and response bit, true if packet is from initiator,
+ * false otherwise, not actual "command" and "response"
+ * @param dlci DLCI of this pcaket, RFCOMM_MX_DLCI for multiplexer control
+ * @return address field
+ */
+uint8_t GetAddressField(bool ea, bool cr, uint8_t dlci);
+
+/**
+ * Create control field in RFCOMM packet
+ *
+ * @param pf Poll/Finish bit, normally 1 for multiplexer control channel
+ * @param frame_type frame type
+ * @return control field
+ */
+uint8_t GetControlField(bool pf, uint8_t frame_type);
+
+/**
+ * Create Multiplexer control channel parameter negotiation frame
+ *
+ * @param dlci DLCI to be configured
+ * @param i_bits i bits
+ * @param cl_bits cl bits
+ * @param priority priority
+ * @param timer_value timer value
+ * @param rfcomm_mtu rfcomm mtu
+ * @param max_num_retransmission maximum number of retransmission
+ * @param err_recovery_window_k error recovery window k
+ * @return vector of bytes of this frame
+ */
+std::vector<uint8_t> CreateMccPnFrame(uint8_t dlci, uint8_t i_bits,
+ uint8_t cl_bits, uint8_t priority,
+ uint8_t timer_value, uint16_t rfcomm_mtu,
+ uint8_t max_num_retransmission,
+ uint8_t err_recovery_window_k);
+/**
+ * Create Multiplexer Control Modem Status Configuration Frame
+ *
+ * @param dlci DLCI to be configured
+ * @param fc flow control
+ * @param rtc ready to communicate
+ * @param rtr ready to receive
+ * @param ic incoming call indicator
+ * @param dv is data valid
+ * @return vector of bytes
+ */
+std::vector<uint8_t> CreateMccMscFrame(uint8_t dlci, bool fc, bool rtc,
+ bool rtr, bool ic, bool dv);
+
+/**
+ * Create Multiplexer Control Frame
+ *
+ * @param command_type type of command
+ * @param cr command or response flag, true when this is a command, false when
+ * this is a response, regardless of connection direction
+ * @param data frame data
+ * @return vector of bytes
+ */
+std::vector<uint8_t> CreateMultiplexerControlFrame(
+ uint8_t command_type, bool cr, const std::vector<uint8_t>& data);
+
+/**
+ * Create a general RFCOMM packet
+ *
+ * @param address address byte
+ * @param control control byte
+ * @param credits number of credits, <= 0 will cause this to be ignored
+ * @param data frame data
+ * @return vector of bytes
+ */
+std::vector<uint8_t> CreateRfcommPacket(uint8_t address, uint8_t control,
+ int credits,
+ const std::vector<uint8_t>& data);
+/*
+ * Various shortcut for getting frequently used packets
+ */
+
+/**
+ * Create SABM packet that is used to connect to a service channel number in a
+ * multiplexer
+ *
+ * @param dlci DLCI to be connected
+ * @param l2cap_lcid L2CAP channel ID
+ * @param acl_handle ACL handle
+ * @return vector of bytes of unwrapped ACL packet
+ */
+std::vector<uint8_t> CreateQuickSabmPacket(uint8_t dlci, uint16_t l2cap_lcid,
+ uint16_t acl_handle);
+
+/**
+ * Create UA packet that is used to acknowledge service channel connection
+ *
+ * @param dlci DLCI to be connected
+ * @param l2cap_lcid L2CAP channel ID
+ * @param acl_handle ACL handle
+ * @return vector of bytes of unwrapped ACL packet
+ */
+std::vector<uint8_t> CreateQuickUaPacket(uint8_t dlci, uint16_t l2cap_lcid,
+ uint16_t acl_handle);
+
+/**
+ * Create parameter negotiation packet used to setup parameters for a DLCI
+ *
+ * @param rfc_cr RFCOMM command/response bit, true of initiator
+ * @param target_dlci DLCI to be configured
+ * @param mx_cr multiplexer command or reponse, regardless of initiator
+ * @param rfc_mtu RFCOMM mtu to be used for DLCI
+ * @param cl CL bit
+ * @param priority prirority
+ * @param k error recovery window k
+ * @param l2cap_lcid L2CAP channel ID
+ * @param acl_handle ACL handle
+ * @return vector of bytes of unwrapped ACL packet
+ */
+std::vector<uint8_t> CreateQuickPnPacket(bool rfc_cr, uint8_t target_dlci,
+ bool mx_cr, uint16_t rfc_mtu,
+ uint8_t cl, uint8_t priority,
+ uint8_t k, uint16_t l2cap_lcid,
+ uint16_t acl_handle);
+
+/**
+ * Create modem signal control packet
+ *
+ * @param rfc_cr RFCOMM command/response bit, true of initiator
+ * @param dlci DLCI to be configured
+ * @param l2cap_lcid L2CAP channel ID
+ * @param acl_handle ACL handle
+ * @param mx_cr multiplexer command or reponse, regardless of initiator
+ * @param fc flow control
+ * @param rtc ready to communicate
+ * @param rtr ready to receive
+ * @param ic incoming call indicator
+ * @param dv data valid
+ * @return vector of bytes of unwrapped ACL packet
+ */
+std::vector<uint8_t> CreateQuickMscPacket(bool rfc_cr, uint8_t dlci,
+ uint16_t l2cap_lcid,
+ uint16_t acl_handle, bool mx_cr,
+ bool fc, bool rtc, bool rtr, bool ic,
+ bool dv);
+
+/**
+ * Create a quick RFCOMM data packet
+ *
+ * @param dlci DLCI of this packet
+ * @param cr command or control, true for initiator
+ * @param l2cap_lcid L2CAP channel ID
+ * @param acl_handle ACL handle
+ * @param credits number of credits
+ * @param data data bytes
+ * @return vector of bytes of unwrapped ACL packet
+ */
+std::vector<uint8_t> CreateQuickDataPacket(uint8_t dlci, bool cr,
+ uint16_t l2cap_lcid,
+ uint16_t acl_handle, int credits,
+ const std::vector<uint8_t>& data);
+
+/**
+ * Create a quick RFCOMM data packet
+ *
+ * @param dlci DLCI of this packet
+ * @param cr command or control, true for initiator
+ * @param l2cap_lcid L2CAP channel ID
+ * @param acl_handle ACL handle
+ * @param credits number of credits
+ * @param str message in string format
+ * @return vector of bytes of unwrapped ACL packet
+ */
+std::vector<uint8_t> CreateQuickDataPacket(uint8_t dlci, bool cr,
+ uint16_t l2cap_lcid,
+ uint16_t acl_handle, int credits,
+ const std::string& str);
+
+} // namespace rfcomm
+} // namespace bluetooth
\ No newline at end of file
diff --git a/stack/test/rfcomm/stack_rfcomm_test_utils_test.cc b/stack/test/rfcomm/stack_rfcomm_test_utils_test.cc
new file mode 100644
index 0000000..3d9a2d5
--- /dev/null
+++ b/stack/test/rfcomm/stack_rfcomm_test_utils_test.cc
@@ -0,0 +1,246 @@
+/******************************************************************************
+ *
+ * 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 <string>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "rfcdefs.h"
+#include "stack_rfcomm_test_utils.h"
+#include "stack_test_packet_utils.h"
+
+namespace {
+
+using testing::ElementsAreArray;
+
+using bluetooth::CreateL2capDataPacket;
+using bluetooth::CreateAclPacket;
+using bluetooth::AllocateWrappedIncomingL2capAclPacket;
+
+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;
+
+const uint8_t kIncomingSabmChannel0[] = {0x08, 0x20, 0x08, 0x00, 0x04, 0x00,
+ 0x5c, 0x00, 0x03, 0x3f, 0x01, 0x1c};
+
+TEST(RfcommTestPacketGeneratorTest, TestGenerateSabmChannel0Packet) {
+ uint8_t dlci = GetDlci(false, 0);
+ EXPECT_EQ(dlci, 0);
+ uint8_t address_field = GetAddressField(true, true, dlci);
+ EXPECT_EQ(address_field, 0x03);
+ uint8_t control_field = GetControlField(true, RFCOMM_SABME);
+ EXPECT_EQ(control_field, 0x3F);
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, -1, {});
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(0x005c, rfcomm_packet);
+ std::vector<uint8_t> acl_packet =
+ CreateAclPacket(0x0008, 0b10, 0b00, l2cap_packet);
+ EXPECT_THAT(acl_packet, ElementsAreArray(kIncomingSabmChannel0));
+}
+
+TEST(RfcommTestPacketGeneratorTest, TestQuickGenerateSabmChannel0Packet) {
+ EXPECT_THAT(CreateQuickSabmPacket(RFCOMM_MX_DLCI, 0x005c, 0x0008),
+ ElementsAreArray(kIncomingSabmChannel0));
+}
+
+const uint8_t kOutgoingUaChannel0[] = {0x08, 0x20, 0x08, 0x00, 0x04, 0x00,
+ 0x00, 0x17, 0x03, 0x73, 0x01, 0xd7};
+
+TEST(RfcommTestPacketGeneratorTest, TestGenerateUaPacket) {
+ uint8_t dlci = GetDlci(false, 0);
+ EXPECT_EQ(dlci, 0);
+ uint8_t address_field = GetAddressField(true, true, dlci);
+ EXPECT_EQ(address_field, 0x03);
+ uint8_t control_field = GetControlField(true, RFCOMM_UA);
+ EXPECT_EQ(control_field, 0x73);
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, -1, {});
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(0x1700, rfcomm_packet);
+ std::vector<uint8_t> acl_packet =
+ CreateAclPacket(0x0008, 0b10, 0b00, l2cap_packet);
+ EXPECT_THAT(acl_packet, ElementsAreArray(kOutgoingUaChannel0));
+}
+
+TEST(RfcommTestPacketGeneratorTest, TestQuickGenerateUaPacket) {
+ EXPECT_THAT(CreateQuickUaPacket(RFCOMM_MX_DLCI, 0x1700, 0x0008),
+ ElementsAreArray(kOutgoingUaChannel0));
+}
+
+const uint8_t kIncomingUihPnSetChannelTo3[] = {
+ 0x08, 0x20, 0x12, 0x00, 0x0e, 0x00, 0x5c, 0x00, 0x03, 0xef, 0x15,
+ 0x83, 0x11, 0x06, 0xf0, 0x00, 0x00, 0x74, 0x03, 0x00, 0x01, 0x70};
+
+TEST(RfcommTestPacketGeneratorTest, TestGenerateUihPnSetChannel3Packet) {
+ uint8_t dlci = GetDlci(false, 0);
+ EXPECT_EQ(dlci, 0);
+ uint8_t address_field = GetAddressField(true, true, dlci);
+ EXPECT_EQ(address_field, 0x03);
+ uint8_t control_field = GetControlField(false, RFCOMM_UIH);
+ EXPECT_EQ(control_field, 0xEF);
+ uint8_t new_dlci = GetDlci(false, 3);
+ EXPECT_EQ(new_dlci, 6);
+ std::vector<uint8_t> mcc_pn_data =
+ CreateMccPnFrame(new_dlci, 0x0, 0xF, 0, 0, 884, 0, 1);
+ std::vector<uint8_t> mcc_payload =
+ CreateMultiplexerControlFrame(0x20, true, mcc_pn_data);
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, -1, mcc_payload);
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(0x005c, rfcomm_packet);
+ std::vector<uint8_t> acl_packet =
+ CreateAclPacket(0x0008, 0b10, 0b00, l2cap_packet);
+ EXPECT_THAT(acl_packet, ElementsAreArray(kIncomingUihPnSetChannelTo3));
+}
+
+TEST(RfcommTestPacketGeneratorTest, TestQuickGenerateUihPnSetChannel3Packet) {
+ EXPECT_THAT(CreateQuickPnPacket(true, GetDlci(false, 3), true, 884, 0xF, 0, 1,
+ 0x005c, 0x0008),
+ ElementsAreArray(kIncomingUihPnSetChannelTo3));
+}
+
+const uint8_t kIncomingSabmChannel3[] = {0x08, 0x20, 0x08, 0x00, 0x04, 0x00,
+ 0x5c, 0x00, 0x1b, 0x3f, 0x01, 0xd3};
+
+TEST(RfcommTestPacketGeneratorTest, TestGenerateSabmChannel3Packet) {
+ uint8_t dlci = GetDlci(false, 3);
+ EXPECT_EQ(dlci, 6);
+ uint8_t address_field = GetAddressField(true, true, dlci);
+ EXPECT_EQ(address_field, 0x1b);
+ uint8_t control_field = GetControlField(true, RFCOMM_SABME);
+ EXPECT_EQ(control_field, 0x3F);
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, -1, {});
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(0x005c, rfcomm_packet);
+ std::vector<uint8_t> acl_packet =
+ CreateAclPacket(0x0008, 0b10, 0b00, l2cap_packet);
+ EXPECT_THAT(acl_packet, ElementsAreArray(kIncomingSabmChannel3));
+}
+
+TEST(RfcommTestPacketGeneratorTest, TestGenerateQuickSabmChannel3Packet) {
+ EXPECT_THAT(CreateQuickSabmPacket(GetDlci(false, 3), 0x005c, 0x0008),
+ ElementsAreArray(kIncomingSabmChannel3));
+}
+
+const uint8_t kIncomingUihMscCmdFrame[] = {0x08, 0x20, 0x0c, 0x00, 0x08, 0x00,
+ 0x5c, 0x00, 0x03, 0xef, 0x09, 0xe3,
+ 0x05, 0x1b, 0x8d, 0x70};
+
+TEST(RfcommTestPacketGeneratorTest, TestGenerateUihMscCmdPacket) {
+ uint8_t dlci = GetDlci(false, 0);
+ EXPECT_EQ(dlci, 0);
+ uint8_t address_field = GetAddressField(true, true, dlci);
+ EXPECT_EQ(address_field, 0x03);
+ uint8_t control_field = GetControlField(false, RFCOMM_UIH);
+ EXPECT_EQ(control_field, 0xEF);
+ uint8_t new_dlci = GetDlci(false, 3);
+ EXPECT_EQ(new_dlci, 6);
+ std::vector<uint8_t> mcc_msc_data =
+ CreateMccMscFrame(new_dlci, false, true, true, false, true);
+ std::vector<uint8_t> mcc_payload =
+ CreateMultiplexerControlFrame(0x38, true, mcc_msc_data);
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, -1, mcc_payload);
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(0x005c, rfcomm_packet);
+ std::vector<uint8_t> acl_packet =
+ CreateAclPacket(0x0008, 0b10, 0b00, l2cap_packet);
+ EXPECT_THAT(acl_packet, ElementsAreArray(kIncomingUihMscCmdFrame));
+}
+
+TEST(RfcommTestPacketGeneratorTest, TestQuickGenerateUihMscCmdPacket) {
+ EXPECT_THAT(CreateQuickMscPacket(true, GetDlci(false, 3), 0x005c, 0x0008,
+ true, false, true, true, false, true),
+ ElementsAreArray(kIncomingUihMscCmdFrame));
+}
+
+const uint8_t kIncomingUihMscResponseFrame[] = {
+ 0x08, 0x20, 0x0c, 0x00, 0x08, 0x00, 0x5c, 0x00,
+ 0x03, 0xef, 0x09, 0xe1, 0x05, 0x1b, 0x8d, 0x70};
+
+TEST(RfcommTestPacketGeneratorTest, TestGenerateUihMscResponsePacket) {
+ uint8_t dlci = GetDlci(false, 0);
+ EXPECT_EQ(dlci, 0);
+ uint8_t address_field = GetAddressField(true, true, dlci);
+ EXPECT_EQ(address_field, 0x03);
+ uint8_t control_field = GetControlField(false, RFCOMM_UIH);
+ EXPECT_EQ(control_field, 0xEF);
+ uint8_t new_dlci = GetDlci(false, 3);
+ EXPECT_EQ(new_dlci, 6);
+ std::vector<uint8_t> mcc_msc_data =
+ CreateMccMscFrame(new_dlci, false, true, true, false, true);
+ std::vector<uint8_t> mcc_payload =
+ CreateMultiplexerControlFrame(0x38, false, mcc_msc_data);
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, -1, mcc_payload);
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(0x005c, rfcomm_packet);
+ std::vector<uint8_t> acl_packet =
+ CreateAclPacket(0x0008, 0b10, 0b00, l2cap_packet);
+ EXPECT_THAT(acl_packet, ElementsAreArray(kIncomingUihMscResponseFrame));
+}
+
+TEST(RfcommTestPacketGeneratorTest, TestQuickGenerateUihMscResponsePacket) {
+ EXPECT_THAT(CreateQuickMscPacket(true, GetDlci(false, 3), 0x005c, 0x0008,
+ false, false, true, true, false, true),
+ ElementsAreArray(kIncomingUihMscResponseFrame));
+}
+
+const uint8_t kIncomingBrsfFrame[] = {0x08, 0x20, 0x15, 0x00, 0x11, 0x00, 0x5c,
+ 0x00, 0x1b, 0xff, 0x19, 0x06, 0x41, 0x54,
+ 0x2b, 0x42, 0x52, 0x53, 0x46, 0x3d, 0x39,
+ 0x35, 0x39, 0x0d, 0x93};
+
+TEST(RfcommTestPacketGeneratorTest, TestGenerateDataPacket) {
+ uint8_t dlci = GetDlci(false, 3);
+ EXPECT_EQ(dlci, 6);
+ uint8_t address_field = GetAddressField(true, true, dlci);
+ EXPECT_EQ(address_field, 0x1B);
+ uint8_t control_field = GetControlField(true, RFCOMM_UIH);
+ EXPECT_EQ(control_field, 0xFF);
+ const std::string data_str = "AT+BRSF=959\r";
+ const std::vector<uint8_t> data(data_str.begin(), data_str.end());
+ std::vector<uint8_t> rfcomm_packet =
+ CreateRfcommPacket(address_field, control_field, 6, data);
+ std::vector<uint8_t> l2cap_packet =
+ CreateL2capDataPacket(0x005c, rfcomm_packet);
+ std::vector<uint8_t> acl_packet =
+ CreateAclPacket(0x0008, 0b10, 0b00, l2cap_packet);
+ EXPECT_THAT(acl_packet, ElementsAreArray(kIncomingBrsfFrame));
+}
+
+TEST(RfcommTestPacketGeneratorTest, TestQuickGenerateDataPacket) {
+ EXPECT_THAT(CreateQuickDataPacket(GetDlci(false, 3), true, 0x005c, 0x0008, 6,
+ "AT+BRSF=959\r"),
+ ElementsAreArray(kIncomingBrsfFrame));
+}
+
+} // namespace
\ No newline at end of file
diff --git a/test/run_unit_tests.sh b/test/run_unit_tests.sh
index 9fc0676..066e8e1 100755
--- a/test/run_unit_tests.sh
+++ b/test/run_unit_tests.sh
@@ -20,10 +20,11 @@
net_test_btu_message_loop
net_test_osi
net_test_performance
+ net_test_stack_rfcomm
)
known_remote_tests=(
- net_test_rfcomm
+ net_test_rfcomm_suite
)
diff --git a/test/suite/Android.bp b/test/suite/Android.bp
index fb501c7..ed5b531 100644
--- a/test/suite/Android.bp
+++ b/test/suite/Android.bp
@@ -28,7 +28,7 @@
// Bluetooth test suite for target
// ========================================================
cc_test {
- name: "net_test_rfcomm",
+ name: "net_test_rfcomm_suite",
defaults: ["fluoride_defaults"],
include_dirs: ["system/bt"],
srcs: [