OSDN Git Service

HID: Fix forced disconnection flow.
authorIvan Podogov <ginkage@google.com>
Thu, 19 Jan 2017 17:19:37 +0000 (17:19 +0000)
committerAndre Eisenbach <eisenbach@google.com>
Wed, 25 Jan 2017 00:07:45 +0000 (00:07 +0000)
In some cases, we end up in a state where we can neither
connect nor forcefully end connection, and will require disabling
the Bluetooth adapter to fix this state.

When a device is taking too long to connect (or out of range),
the user may want to cancel the connection by calling disconnect
method, which will be ignored in any state other than
BTA_HD_CONN_ST. It is a lot better to immediately cease the
connection process at this point, so:
- BTA_HD_API_DISCONNECT_EVT is now not ignored in BTA_HD_IDLE_ST;
- bta_hd_disconnect_act now reports a correct MAC address during
    disconnection (it used to send 00:00:00:00:00:00 before);
- HidDevDisconnect now allows to forcefully end the connection,
    and does it in exactly the same way we handle the errors.

When L2CAP connection fails, both hidd_l2cif_config_ind and
hidd_l2cif_config_cfm set conn_state to HID_CONN_STATE_UNUSED,
which is immediately overwritten by the hidd_conn_disconnect call
(it will set conn_state to HID_CONN_STATE_DISCONNECTING, because
ctrl_cid != 0 in both cases), thus making any subsequent calls to
connect failing with "already connecting" error. More than that,
all functions send the HID_DHOST_EVT_CLOSE event when failing,
which is, again, ignored in the BTA_HD_IDLE_ST state. So:
- BTA_HD_INT_CLOSE_EVT is now not ignored in BTA_HD_IDLE_ST;
- conn_state is set to HID_CONN_STATE_UNUSED after the call to
    hidd_conn_disconnect, but before sending the close event.

Test: Build, run, connect/disconnect multiple times.
Change-Id: I85bb03f760bb9a6fd4c1b944d515232c1be12300

bta/hd/bta_hd_act.cc
bta/hd/bta_hd_main.cc
stack/hid/hidd_api.cc
stack/hid/hidd_conn.cc

index 613d298..da36482 100644 (file)
@@ -305,10 +305,10 @@ extern void bta_hd_disconnect_act(UNUSED_ATTR tBTA_HD_DATA* p_data) {
     return;
   }
 
-  bdcpy(cback_data.conn.bda, bta_hd_cb.bd_addr);
-  cback_data.conn.status = BTHD_CONN_STATE_DISCONNECTING;
-
-  bta_hd_cb.p_cback(BTA_HD_CONN_STATE_EVT, &cback_data);
+  if (HID_DevGetDevice(&cback_data.conn.bda) == HID_SUCCESS) {
+    cback_data.conn.status = BTHD_CONN_STATE_DISCONNECTING;
+    bta_hd_cb.p_cback(BTA_HD_CONN_STATE_EVT, &cback_data);
+  }
 }
 
 /*******************************************************************************
index 3e4fa57..02577f6 100644 (file)
@@ -122,14 +122,14 @@ const uint8_t bta_hd_st_idle[][BTA_HD_NUM_COLS] = {
     /* BTA_HD_API_REGISTER_APP_EVT   */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST},
     /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_UNREGISTER_ACT, BTA_HD_INIT_ST},
     /* BTA_HD_API_CONNECT_EVT        */ {BTA_HD_CONNECT_ACT, BTA_HD_IDLE_ST},
-    /* BTA_HD_API_DISCONNECT_EVT     */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST},
+    /* BTA_HD_API_DISCONNECT_EVT     */ {BTA_HD_DISCONNECT_ACT, BTA_HD_IDLE_ST},
     /* BTA_HD_API_ADD_DEVICE_EVT     */ {BTA_HD_ADD_DEVICE_ACT, BTA_HD_IDLE_ST},
     /* BTA_HD_API_REMOVE_DEVICE_EVT  */ {BTA_HD_REMOVE_DEVICE_ACT, BTA_HD_IDLE_ST},
     /* BTA_HD_API_SEND_REPORT_EVT    */ {BTA_HD_SEND_REPORT_ACT, BTA_HD_IDLE_ST},
     /* BTA_HD_API_REPORT_ERROR_EVT   */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST},
     /* BTA_HD_API_VC_UNPLUG_EVT      */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST},
     /* BTA_HD_INT_OPEN_EVT           */ {BTA_HD_OPEN_ACT, BTA_HD_CONN_ST},
-    /* BTA_HD_INT_CLOSE_EVT          */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST},
+    /* BTA_HD_INT_CLOSE_EVT          */ {BTA_HD_CLOSE_ACT, BTA_HD_IDLE_ST},
     /* BTA_HD_INT_INTR_DATA_EVT      */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST},
     /* BTA_HD_INT_GET_REPORT_EVT     */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST},
     /* BTA_HD_INT_SET_REPORT_EVT     */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST},
index adaba58..bce5ef5 100644 (file)
@@ -496,6 +496,14 @@ tHID_STATUS HID_DevDisconnect(void) {
   }
 
   if (hd_cb.device.state == HIDD_DEV_NO_CONN) {
+    /* If we are still trying to connect, just close the connection. */
+    if (hd_cb.device.conn.conn_state != HID_CONN_STATE_UNUSED) {
+      tHID_STATUS ret = hidd_conn_disconnect();
+      hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED;
+      hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE,
+                     HID_ERR_DISCONNECTING, NULL);
+      return ret;
+    }
     return HID_ERR_NO_CONNECTION;
   }
 
index 19f81e7..c76e545 100644 (file)
@@ -373,8 +373,8 @@ static void hidd_l2cif_config_ind(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) {
       p_hcon->disc_reason = HID_L2CAP_CONN_FAIL;
       if ((p_hcon->intr_cid =
                L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) {
-        p_hcon->conn_state = HID_CONN_STATE_UNUSED;
         hidd_conn_disconnect();
+        p_hcon->conn_state = HID_CONN_STATE_UNUSED;
 
         HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR",
                            __func__);
@@ -456,8 +456,8 @@ static void hidd_l2cif_config_cfm(uint16_t cid, tL2CAP_CFG_INFO* p_cfg) {
       p_hcon->disc_reason = HID_L2CAP_CONN_FAIL;
       if ((p_hcon->intr_cid =
                L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) {
-        p_hcon->conn_state = HID_CONN_STATE_UNUSED;
         hidd_conn_disconnect();
+        p_hcon->conn_state = HID_CONN_STATE_UNUSED;
 
         HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR",
                            __func__);