Race condition in opening/closing of uhid file descriptor
When HID host is connected, uhid file descriptor is opened from bta_hh
in main thread. However, when it is disconnected, uhid file descriptor
is closed from btif_hh in jni thread.
If the HID device gets disconnected and connected in quick succession,
the race condition can cause connection to fail.
Bug: 264988156
Test: Manual
Change-Id: I9ff6196eeebdd0a1dd5245f29a174c4b28dbe1b6
diff --git a/system/bta/hh/bta_hh_act.cc b/system/bta/hh/bta_hh_act.cc
index 668dcd1..c237534 100644
--- a/system/bta/hh/bta_hh_act.cc
+++ b/system/bta/hh/bta_hh_act.cc
@@ -617,13 +617,12 @@
/* increase connection number */
bta_hh_cb.cnt_num++;
- /* initialize device driver */
- bta_hh_co_open(p_cb->hid_handle, p_cb->sub_class, p_cb->attr_mask,
- p_cb->app_id);
-
conn.status = p_cb->status;
conn.le_hid = p_cb->is_le_device;
conn.scps_supported = p_cb->scps_supported;
+ conn.sub_class = p_cb->sub_class;
+ conn.attr_mask = p_cb->attr_mask;
+ conn.app_id = p_cb->app_id;
BTM_LogHistory(kBtmLogTag, p_cb->addr, "Opened",
base::StringPrintf(
@@ -634,26 +633,25 @@
{
/* inform role manager */
bta_sys_conn_open(BTA_ID_HH, p_cb->app_id, p_cb->addr);
- }
- /* set protocol mode when not default report mode */
- if (p_cb->mode != BTA_HH_PROTO_RPT_MODE
- && !p_cb->is_le_device
- ) {
- if ((HID_HostWriteDev(dev_handle, HID_TRANS_SET_PROTOCOL,
- HID_PAR_PROTOCOL_BOOT_MODE, 0, 0, NULL)) !=
- HID_SUCCESS) {
- /* HID connection is up, while SET_PROTO fail */
- conn.status = BTA_HH_ERR_PROTO;
- (*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH*)&conn);
- } else {
- conn.status = BTA_HH_OK;
- p_cb->w4_evt = BTA_HH_OPEN_EVT;
- }
- } else
- (*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH*)&conn);
+ /* set protocol mode when not default report mode */
+ if (p_cb->mode != BTA_HH_PROTO_RPT_MODE) {
+ tHID_STATUS status =
+ HID_HostWriteDev(dev_handle, HID_TRANS_SET_PROTOCOL,
+ HID_PAR_PROTOCOL_BOOT_MODE, 0, 0, NULL);
+
+ if (status == HID_SUCCESS) {
+ p_cb->w4_evt = BTA_HH_SET_PROTO_EVT;
+ } else {
+ /* HID connection is up, while SET_PROTO fail */
+ conn.status = BTA_HH_ERR_PROTO;
+ }
+ }
+ }
p_cb->incoming_conn = false;
p_cb->incoming_hid_handle = BTA_HH_INVALID_HANDLE;
+
+ (*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH*)&conn);
}
/*******************************************************************************
*
diff --git a/system/bta/include/bta_hh_api.h b/system/bta/include/bta_hh_api.h
index 2d09a3e..1265524 100644
--- a/system/bta/include/bta_hh_api.h
+++ b/system/bta/include/bta_hh_api.h
@@ -239,9 +239,11 @@
RawAddress bda; /* HID device bd address */
tBTA_HH_STATUS status; /* operation status */
uint8_t handle; /* device handle */
- bool le_hid; /* is LE devices? */
- bool scps_supported; /* scan parameter service supported */
-
+ bool le_hid; /* is LE devices? */
+ bool scps_supported; /* scan parameter service supported */
+ uint8_t sub_class; /* Cod sub class */
+ uint16_t attr_mask; /* attribute mask */
+ uint8_t app_id;
} tBTA_HH_CONN;
typedef tBTA_HH_CONN tBTA_HH_DEV_INFO;
diff --git a/system/bta/include/bta_hh_co.h b/system/bta/include/bta_hh_co.h
index 21a1662..2814aaa 100644
--- a/system/bta/include/bta_hh_co.h
+++ b/system/bta/include/bta_hh_co.h
@@ -62,10 +62,10 @@
* opened, and application may do some device specific
* initialization.
*
- * Returns void.
+ * Returns True if platform specific initialization is successful
*
******************************************************************************/
-void bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, uint16_t attr_mask,
+bool bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, uint16_t attr_mask,
uint8_t app_id);
/*******************************************************************************
diff --git a/system/btif/co/bta_hh_co.cc b/system/btif/co/bta_hh_co.cc
index 8e02889..1fb0668 100644
--- a/system/btif/co/bta_hh_co.cc
+++ b/system/btif/co/bta_hh_co.cc
@@ -352,6 +352,22 @@
return uhid_write(fd, &ev);
}
+static bool uhid_fd_open(btif_hh_device_t* p_dev) {
+ if (p_dev->fd < 0) {
+ p_dev->fd = open(dev_path, O_RDWR | O_CLOEXEC);
+ if (p_dev->fd < 0) {
+ LOG_ERROR("Failed to open uhid, err:%s", strerror(errno));
+ return false;
+ }
+ }
+
+ if (p_dev->hh_keep_polling == 0) {
+ p_dev->hh_keep_polling = 1;
+ p_dev->hh_poll_thread_id = create_thread(btif_hh_poll_event_thread, p_dev);
+ }
+ return true;
+}
+
/*******************************************************************************
*
* Function bta_hh_co_open
@@ -359,17 +375,16 @@
* Description When connection is opened, this call-out function is executed
* by HH to do platform specific initialization.
*
- * Returns void.
+ * Returns True if platform specific initialization is successful
******************************************************************************/
-void bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class,
+bool bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class,
tBTA_HH_ATTR_MASK attr_mask, uint8_t app_id) {
uint32_t i;
btif_hh_device_t* p_dev = NULL;
if (dev_handle == BTA_HH_INVALID_HANDLE) {
- APPL_TRACE_WARNING("%s: Oops, dev_handle (%d) is invalid...", __func__,
- dev_handle);
- return;
+ LOG_WARN("dev_handle (%d) is invalid", dev_handle);
+ return false;
}
for (i = 0; i < BTIF_HH_MAX_HID; i++) {
@@ -377,25 +392,15 @@
if (p_dev->dev_status != BTHH_CONN_STATE_UNKNOWN &&
p_dev->dev_handle == dev_handle) {
// We found a device with the same handle. Must be a device reconnected.
- APPL_TRACE_WARNING(
- "%s: Found an existing device with the same handle dev_status=%d, "
+ LOG_INFO(
+ "Found an existing device with the same handle dev_status=%d, "
"address=%s, attr_mask=0x%04x, sub_class=0x%02x, app_id=%d",
- __func__, p_dev->dev_status, ADDRESS_TO_LOGGABLE_CSTR(p_dev->bd_addr),
+ p_dev->dev_status, ADDRESS_TO_LOGGABLE_CSTR(p_dev->bd_addr),
p_dev->attr_mask, p_dev->sub_class, p_dev->app_id);
- if (p_dev->fd < 0) {
- p_dev->fd = open(dev_path, O_RDWR | O_CLOEXEC);
- if (p_dev->fd < 0) {
- APPL_TRACE_ERROR("%s: Error: failed to open uhid, err:%s", __func__,
- strerror(errno));
- return;
- } else
- APPL_TRACE_DEBUG("%s: uhid fd = %d", __func__, p_dev->fd);
+ if (!uhid_fd_open(p_dev)) {
+ return false;
}
-
- p_dev->hh_keep_polling = 1;
- p_dev->hh_poll_thread_id =
- create_thread(btif_hh_poll_event_thread, p_dev);
break;
}
p_dev = NULL;
@@ -406,6 +411,14 @@
for (i = 0; i < BTIF_HH_MAX_HID; i++) {
if (btif_hh_cb.devices[i].dev_status == BTHH_CONN_STATE_UNKNOWN) {
p_dev = &btif_hh_cb.devices[i];
+ p_dev->fd = -1;
+ p_dev->hh_keep_polling = 0;
+
+ // This is a new device, open the uhid driver now.
+ if (!uhid_fd_open(p_dev)) {
+ return false;
+ }
+
p_dev->dev_handle = dev_handle;
p_dev->attr_mask = attr_mask;
p_dev->sub_class = sub_class;
@@ -413,27 +426,14 @@
p_dev->local_vup = false;
btif_hh_cb.device_num++;
- // This is a new device,open the uhid driver now.
- p_dev->fd = open(dev_path, O_RDWR | O_CLOEXEC);
- if (p_dev->fd < 0) {
- APPL_TRACE_ERROR("%s: Error: failed to open uhid, err:%s", __func__,
- strerror(errno));
- return;
- } else {
- APPL_TRACE_DEBUG("%s: uhid fd = %d", __func__, p_dev->fd);
- p_dev->hh_keep_polling = 1;
- p_dev->hh_poll_thread_id =
- create_thread(btif_hh_poll_event_thread, p_dev);
- }
-
break;
}
}
}
if (p_dev == NULL) {
- APPL_TRACE_ERROR("%s: Error: too many HID devices are connected", __func__);
- return;
+ LOG_ERROR("Too many HID devices are connected");
+ return false;
}
p_dev->dev_status = BTHH_CONN_STATE_CONNECTED;
@@ -444,7 +444,8 @@
CHECK(p_dev->set_rpt_id_queue);
#endif // ENABLE_UHID_SET_REPORT
- APPL_TRACE_DEBUG("%s: Return device status %d", __func__, p_dev->dev_status);
+ LOG_DEBUG("Return device status %d", p_dev->dev_status);
+ return true;
}
/*******************************************************************************
diff --git a/system/btif/src/btif_hh.cc b/system/btif/src/btif_hh.cc
index 87df279..451cee1 100644
--- a/system/btif/src/btif_hh.cc
+++ b/system/btif/src/btif_hh.cc
@@ -63,9 +63,6 @@
static int btif_hh_keylockstates = 0; // The current key state of each key
-// TODO This is duplicated in header file with different value
-#define BTIF_HH_DEV_DISCONNECTED 3
-
#define BTIF_TIMEOUT_VUP_MS (3 * 1000)
/* HH request events */
@@ -360,7 +357,7 @@
*
* Returns void
******************************************************************************/
-void btif_hh_stop_vup_timer(RawAddress* bd_addr) {
+static void btif_hh_stop_vup_timer(RawAddress* bd_addr) {
btif_hh_device_t* p_dev = btif_hh_find_connected_dev_by_bda(*bd_addr);
if (p_dev != NULL) {
@@ -377,7 +374,7 @@
*
* Returns void
******************************************************************************/
-void btif_hh_start_vup_timer(const RawAddress* bd_addr) {
+static void btif_hh_start_vup_timer(const RawAddress* bd_addr) {
BTIF_TRACE_DEBUG("%s", __func__);
btif_hh_device_t* p_dev = btif_hh_find_connected_dev_by_bda(*bd_addr);
@@ -389,6 +386,66 @@
btif_hh_timer_timeout, p_dev);
}
+static void hh_connect_complete(uint8_t handle, RawAddress& bda,
+ BTIF_HH_STATUS status) {
+ bthh_connection_state_t state = BTHH_CONN_STATE_CONNECTED;
+ btif_hh_cb.status = status;
+
+ if (status != BTIF_HH_DEV_CONNECTED) {
+ state = BTHH_CONN_STATE_DISCONNECTED;
+ BTA_HhClose(handle);
+ }
+ HAL_CBACK(bt_hh_callbacks, connection_state_cb, &bda, state);
+}
+
+static void hh_open_handler(tBTA_HH_CONN& conn) {
+ LOG_DEBUG("status = %d, handle = %d", conn.status, conn.handle);
+
+ HAL_CBACK(bt_hh_callbacks, connection_state_cb, (RawAddress*)&conn.bda,
+ BTHH_CONN_STATE_CONNECTING);
+ btif_hh_cb.pending_conn_address = RawAddress::kEmpty;
+
+ if (conn.status != BTA_HH_OK) {
+ btif_dm_hh_open_failed(&conn.bda);
+ btif_hh_device_t* p_dev = btif_hh_find_dev_by_bda(conn.bda);
+ if (p_dev != NULL) {
+ btif_hh_stop_vup_timer(&(p_dev->bd_addr));
+ p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED;
+ }
+ hh_connect_complete(conn.handle, conn.bda, BTIF_HH_DEV_DISCONNECTED);
+ return;
+ }
+
+ /* Initialize device driver */
+ if (!bta_hh_co_open(conn.handle, conn.sub_class, conn.attr_mask,
+ conn.app_id)) {
+ LOG_WARN("Failed to find the uhid driver");
+ hh_connect_complete(conn.handle, conn.bda, BTIF_HH_DEV_DISCONNECTED);
+ return;
+ }
+
+ btif_hh_device_t* p_dev = btif_hh_find_connected_dev_by_handle(conn.handle);
+ if (p_dev == NULL) {
+ /* The connect request must have come from device side and exceeded the
+ * connected HID device number. */
+ LOG_WARN("Cannot find device with handle %d", conn.handle);
+ hh_connect_complete(conn.handle, conn.bda, BTIF_HH_DEV_DISCONNECTED);
+ return;
+ }
+
+ LOG_INFO("Found device, getting dscp info for handle %d", conn.handle);
+
+ p_dev->bd_addr = conn.bda;
+ p_dev->dev_status = BTHH_CONN_STATE_CONNECTED;
+ hh_connect_complete(conn.handle, conn.bda, BTIF_HH_DEV_CONNECTED);
+ // Send set_idle if the peer_device is a keyboard
+ if (check_cod(&conn.bda, COD_HID_KEYBOARD) ||
+ check_cod(&conn.bda, COD_HID_COMBO)) {
+ BTA_HhSetIdle(conn.handle, 0);
+ }
+ BTA_HhGetDscpInfo(conn.handle);
+}
+
/*******************************************************************************
*
* Function btif_hh_add_added_dev
@@ -801,65 +858,7 @@
break;
case BTA_HH_OPEN_EVT:
- BTIF_TRACE_DEBUG("BTA_HH_OPEN_EVT: status = %d, handle = %d",
- p_data->dev_status.status, p_data->dev_status.handle);
- HAL_CBACK(bt_hh_callbacks, connection_state_cb,
- (RawAddress*)&p_data->conn.bda, BTHH_CONN_STATE_CONNECTING);
- btif_hh_cb.pending_conn_address = RawAddress::kEmpty;
- if (p_data->conn.status == BTA_HH_OK) {
- p_dev = btif_hh_find_connected_dev_by_handle(p_data->conn.handle);
- if (p_dev == NULL) {
- BTIF_TRACE_WARNING(
- "BTA_HH_OPEN_EVT: Error, cannot find device with handle %d",
- p_data->conn.handle);
- btif_hh_cb.status = (BTIF_HH_STATUS)BTIF_HH_DEV_DISCONNECTED;
- // The connect request must come from device side and exceeded the
- // connected
- // HID device number.
- BTA_HhClose(p_data->conn.handle);
- HAL_CBACK(bt_hh_callbacks, connection_state_cb,
- (RawAddress*)&p_data->conn.bda,
- BTHH_CONN_STATE_DISCONNECTED);
- } else if (p_dev->fd < 0) {
- BTIF_TRACE_WARNING(
- "BTA_HH_OPEN_EVT: Error, failed to find the uhid driver...");
- p_dev->bd_addr = p_data->conn.bda;
- // remove the connection and then try again to reconnect from the
- // mouse side to recover
- btif_hh_cb.status = (BTIF_HH_STATUS)BTIF_HH_DEV_DISCONNECTED;
- BTA_HhClose(p_data->conn.handle);
- } else {
- BTIF_TRACE_WARNING(
- "BTA_HH_OPEN_EVT: Found device...Getting dscp info for handle "
- "... %d",
- p_data->conn.handle);
- p_dev->bd_addr = p_data->conn.bda;
- btif_hh_cb.status = (BTIF_HH_STATUS)BTIF_HH_DEV_CONNECTED;
- // Send set_idle if the peer_device is a keyboard
- if (check_cod(&p_data->conn.bda, COD_HID_KEYBOARD) ||
- check_cod(&p_data->conn.bda, COD_HID_COMBO))
- BTA_HhSetIdle(p_data->conn.handle, 0);
- BTA_HhGetDscpInfo(p_data->conn.handle);
- p_dev->dev_status = BTHH_CONN_STATE_CONNECTED;
- HAL_CBACK(bt_hh_callbacks, connection_state_cb, &(p_dev->bd_addr),
- p_dev->dev_status);
- }
- } else {
- RawAddress* bdaddr = &p_data->conn.bda;
- btif_dm_hh_open_failed(bdaddr);
- p_dev = btif_hh_find_dev_by_bda(*bdaddr);
- if (p_dev != NULL) {
- btif_hh_stop_vup_timer(&(p_dev->bd_addr));
- if (p_dev->fd >= 0) {
- bta_hh_co_destroy(p_dev->fd);
- p_dev->fd = -1;
- }
- p_dev->dev_status = BTHH_CONN_STATE_DISCONNECTED;
- }
- HAL_CBACK(bt_hh_callbacks, connection_state_cb,
- (RawAddress*)&p_data->conn.bda, BTHH_CONN_STATE_DISCONNECTED);
- btif_hh_cb.status = (BTIF_HH_STATUS)BTIF_HH_DEV_DISCONNECTED;
- }
+ hh_open_handler(p_data->conn);
break;
case BTA_HH_CLOSE_EVT:
diff --git a/system/test/mock/mock_btif_co_bta_hh_co.cc b/system/test/mock/mock_btif_co_bta_hh_co.cc
index 280fe83..887ef8c 100644
--- a/system/test/mock/mock_btif_co_bta_hh_co.cc
+++ b/system/test/mock/mock_btif_co_bta_hh_co.cc
@@ -57,9 +57,10 @@
const uint8_t* p_rpt, uint16_t len) {
inc_func_call_count(__func__);
}
-void bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class,
+bool bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class,
tBTA_HH_ATTR_MASK attr_mask, uint8_t app_id) {
inc_func_call_count(__func__);
+ return true;
}
void bta_hh_co_send_hid_info(btif_hh_device_t* p_dev, const char* dev_name,
uint16_t vendor_id, uint16_t product_id,